[CalendarServer-changes] [14481] CalendarServer/branches/users/cdaboo/pod2pod-migration
source_changes at macosforge.org
source_changes at macosforge.org
Fri Feb 27 11:59:39 PST 2015
Revision: 14481
http://trac.calendarserver.org//changeset/14481
Author: cdaboo at apple.com
Date: 2015-02-27 11:59:39 -0800 (Fri, 27 Feb 2015)
Log Message:
-----------
Checkpoint: final sharing state sync. Also includes major re-factor of the way home objects are created to allow for multiple with the same onwerUID but different status.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/export.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/twistedcaldav/test/test_wrapping.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/base/datastore/util.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql_external.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql_sharing.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/file.py
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/test/test_store_api.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_schema/current-oracle-dialect.sql
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current.sql
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_51_to_52.sql
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_51_to_52.sql
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_sharing.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_tables.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/test/util.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/work/test/test_revision_cleanup.py
Removed Paths:
-------------
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/schedule.py
CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/test/test_schedule.py
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/export.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/export.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/export.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -203,7 +203,7 @@
for this calendar home.
"""
uid = yield self.getHomeUID(exportService)
- home = yield txn.calendarHomeWithUID(uid, True)
+ home = yield txn.calendarHomeWithUID(uid, create=True)
result = []
if self.collections:
for collection in self.collections:
@@ -303,6 +303,7 @@
fileobj.write(comp.getTextWithTimezones(True))
+
@inlineCallbacks
def exportToDirectory(calendars, dirname):
"""
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/twistedcaldav/test/test_wrapping.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/twistedcaldav/test/test_wrapping.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -120,7 +120,7 @@
record = yield self.directory.recordWithShortName(RecordType.user, u"wsanchez")
uid = record.uid
txn = self.transactionUnderTest()
- home = yield txn.calendarHomeWithUID(uid, True)
+ home = yield txn.calendarHomeWithUID(uid, create=True)
cal = yield home.calendarWithName("calendar")
yield cal.createCalendarObjectWithName(objectName, VComponent.fromString(objectText))
yield self.commit()
@@ -139,7 +139,7 @@
record = yield self.directory.recordWithShortName(RecordType.user, u"wsanchez")
uid = record.uid
txn = self.transactionUnderTest()
- home = yield txn.addressbookHomeWithUID(uid, True)
+ home = yield txn.addressbookHomeWithUID(uid, create=True)
adbk = yield home.addressbookWithName("addressbook")
yield adbk.createAddressBookObjectWithName(objectName, VCComponent.fromString(objectText))
yield self.commit()
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/base/datastore/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/base/datastore/util.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/base/datastore/util.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -86,6 +86,18 @@
return self.delete(key)
+ # Home objects by UID
+
+ def keyForHomeWithUID(self, homeType, ownerUID, status):
+ return "homeWithUID:%s:%s:%s" % (homeType, status, ownerUID)
+
+
+ # Home objects by id
+
+ def keyForHomeWithID(self, homeType, homeResourceID, status):
+ return "homeWithID:%s:%s:%s" % (homeType, status, homeResourceID)
+
+
# Home child objects by name
def keyForObjectWithName(self, homeResourceID, name):
Deleted: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/schedule.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/schedule.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/schedule.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -1,214 +0,0 @@
-# -*- test-case-name: txdav.caldav.datastore.test.test_scheduling -*-
-##
-# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-from zope.interface.declarations import implements
-from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject, \
- ICalendarTransaction, ICalendarStore
-
-from twisted.python.util import FancyEqMixin
-from twisted.python.components import proxyForInterface
-from twisted.internet.defer import inlineCallbacks, returnValue
-
-
-
-class ImplicitTransaction(
- proxyForInterface(ICalendarTransaction,
- originalAttribute="_transaction")):
- """
- Wrapper around an L{ICalendarStoreTransaction}.
- """
-
- def __init__(self, transaction):
- """
- Initialize an L{ImplicitTransaction}.
-
- @type transaction: L{ICalendarStoreTransaction}
- """
- self._transaction = transaction
-
-
- @inlineCallbacks
- def calendarHomeWithUID(self, uid, create=False):
- # FIXME: 'create' flag
- newHome = yield super(ImplicitTransaction, self).calendarHomeWithUID(uid, create)
-# return ImplicitCalendarHome(newHome, self)
- if newHome is None:
- returnValue(None)
- else:
- # FIXME: relay transaction
- returnValue(ImplicitCalendarHome(newHome, None))
-
-
-
-class ImplicitCalendarHome(proxyForInterface(ICalendarHome, "_calendarHome")):
-
- implements(ICalendarHome)
-
- def __init__(self, calendarHome, transaction):
- """
- Initialize L{ImplicitCalendarHome} with an underlying
- calendar home and L{ImplicitTransaction}.
- """
- self._calendarHome = calendarHome
- self._transaction = transaction
-
-
-# def properties(self):
-# # FIXME: wrap?
-# return self._calendarHome.properties()
-
- @inlineCallbacks
- def calendars(self):
- superCalendars = (yield super(ImplicitCalendarHome, self).calendars())
- wrapped = []
- for calendar in superCalendars:
- wrapped.append(ImplicitCalendar(self, calendar))
- returnValue(wrapped)
-
-
- @inlineCallbacks
- def loadCalendars(self):
- superCalendars = (yield super(ImplicitCalendarHome, self).loadCalendars())
- wrapped = []
- for calendar in superCalendars:
- wrapped.append(ImplicitCalendar(self, calendar))
- returnValue(wrapped)
-
-
- def createCalendarWithName(self, name):
- self._calendarHome.createCalendarWithName(name)
-
-
- def removeCalendarWithName(self, name):
- self._calendarHome.removeCalendarWithName(name)
-
-
- @inlineCallbacks
- def calendarWithName(self, name):
- calendar = yield self._calendarHome.calendarWithName(name)
- if calendar is not None:
- returnValue(ImplicitCalendar(self, calendar))
- else:
- returnValue(None)
-
-
- def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, type):
- return self._calendarHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object, type)
-
-
- def getCalendarResourcesForUID(self, uid):
- return self._calendarHome.getCalendarResourcesForUID(uid)
-
-
-
-class ImplicitCalendarObject(object):
- implements(ICalendarObject)
-
- def setComponent(self, component):
- pass
-
-
- def component(self):
- pass
-
-
- def uid(self):
- pass
-
-
- def componentType(self):
- pass
-
-
- def organizer(self):
- pass
-
-
- def properties(self):
- pass
-
-
-
-class ImplicitCalendar(FancyEqMixin,
- proxyForInterface(ICalendar, "_subCalendar")):
-
- compareAttributes = (
- "_subCalendar",
- "_parentHome",
- )
-
- def __init__(self, parentHome, subCalendar):
- self._parentHome = parentHome
- self._subCalendar = subCalendar
- self._supportedComponents = None
-
-# def ownerCalendarHome(self):
-# return self._parentHome
-# def calendarObjects(self):
-# # FIXME: wrap
-# return self._subCalendar.calendarObjects()
-# def calendarObjectWithUID(self, uid): ""
-# def createCalendarObjectWithName(self, name, component):
-# # FIXME: implement most of StoreCalendarObjectResource here!
-# self._subCalendar.createCalendarObjectWithName(name, component)
-# def syncToken(self): ""
-# def calendarObjectsInTimeRange(self, start, end, timeZone): ""
-# def calendarObjectsSinceToken(self, token): ""
-# def properties(self):
-# # FIXME: probably need to wrap this as well
-# return self._subCalendar.properties()
-#
-# def calendarObjectWithName(self, name):
-# #FIXME: wrap
-# return self._subCalendar.calendarObjectWithName(name)
-
-
- def _createCalendarObjectWithNameInternal(self, name, component, internal_state, options=None):
- return self.createCalendarObjectWithName(name, component, options)
-
-
- def setSupportedComponents(self, supported_components):
- """
- Update the database column with the supported components. Technically this should only happen once
- on collection creation, but for migration we may need to change after the fact - hence a separate api.
- """
- self._supportedComponents = supported_components
-
-
- def getSupportedComponents(self):
- return self._supportedComponents
-
-
-
-class ImplicitStore(proxyForInterface(ICalendarStore, "_calendarStore")):
- """
- This is a wrapper around an L{ICalendarStore} that implements implicit
- scheduling.
- """
-
- def __init__(self, calendarStore):
- """
- Create an L{ImplicitStore} wrapped around another
- L{ICalendarStore} provider.
- """
- self._calendarStore = calendarStore
-
-
- def newTransaction(self, label="unlabeled"):
- """
- Wrap an underlying L{ITransaction}.
- """
- return ImplicitTransaction(self._calendarStore.newTransaction(label))
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -52,7 +52,6 @@
pyCalendarTodatetime, parseSQLDateToPyCalendar
from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
from twistedcaldav.instance import InvalidOverriddenInstanceError
-from twistedcaldav.memcacher import Memcacher
from twistedcaldav.timezones import TimezoneException
from txdav.base.propertystore.base import PropertyName
@@ -445,8 +444,6 @@
_notifierPrefix = "CalDAV"
_dataVersionKey = "CALENDAR-DATAVERSION"
- _cacher = Memcacher("SQL.calhome", pickle=True, key_normalization=False)
-
_componentCalendarName = {
"VEVENT": "calendar",
"VTODO": "tasks",
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -14,19 +14,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from txdav.common.datastore.sql_directory import GroupsRecord
-from txdav.caldav.datastore.sql_directory import GroupAttendeeRecord
-from txdav.caldav.datastore.sql_attachment import Attachment, AttachmentLink
"""
SQL backend for CalDAV storage when resources are external.
"""
-from twisted.internet.defer import succeed, inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue
from twext.python.log import Logger
from txdav.caldav.datastore.sql import CalendarHome, Calendar, CalendarObject
+from txdav.caldav.datastore.sql_attachment import Attachment, AttachmentLink
+from txdav.caldav.datastore.sql_directory import GroupAttendeeRecord
from txdav.caldav.icalendarstore import ComponentUpdateState, ComponentRemoveState
+from txdav.common.datastore.sql_directory import GroupsRecord
from txdav.common.datastore.sql_external import CommonHomeExternal, CommonHomeChildExternal, \
CommonObjectResourceExternal
@@ -37,10 +37,10 @@
Wrapper for a CalendarHome that is external and only supports a limited set of operations.
"""
- def __init__(self, transaction, ownerUID, resourceID):
+ def __init__(self, transaction, homeData):
- CalendarHome.__init__(self, transaction, ownerUID)
- CommonHomeExternal.__init__(self, transaction, ownerUID, resourceID)
+ CalendarHome.__init__(self, transaction, homeData)
+ CommonHomeExternal.__init__(self, transaction, homeData)
def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, mode):
@@ -126,13 +126,6 @@
returnValue([(GroupAttendeeRecord.deserialize(item[0]), GroupsRecord.deserialize(item[1]),) for item in raw_results])
- def createdHome(self):
- """
- No children - make this a no-op.
- """
- return succeed(None)
-
-
def splitCalendars(self):
"""
No children.
Deleted: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/test/test_schedule.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/test/test_schedule.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/test/test_schedule.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -1,70 +0,0 @@
-##
-# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Tests for L{txdav.caldav.datastore.scheduling}.
-
-The aforementioned module is intended to eventually support implicit
-scheduling; however, it does not currently. The interim purpose of this module
-and accompanying tests is to effectively test the interface specifications to
-make sure that the common tests don't require anything I{not} specified in the
-interface, so that dynamic proxies specified with a tool like
-C{proxyForInterface} can be used to implement features such as implicit
-scheduling or data caching as middleware in the data-store layer.
-"""
-
-from twisted.trial.unittest import TestCase, SkipTest
-from txdav.caldav.datastore.test.test_file import FileStorageTests
-from txdav.caldav.datastore.schedule import ImplicitStore
-
-simpleEvent = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER:mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-"""
-
-class ImplicitStoreTests(FileStorageTests, TestCase):
- """
- Tests for L{ImplicitSchedulingStore}.
- """
-
- implicitStore = None
-
- def storeUnderTest(self):
- if self.implicitStore is None:
- sut = super(ImplicitStoreTests, self).storeUnderTest()
- self.implicitStore = ImplicitStore(sut)
- return self.implicitStore
-
-
- def skipit(self):
- raise SkipTest("No private attribute tests.")
-
- test_calendarObjectsWithDotFile = skipit
- test_countComponentTypes = skipit
- test_init = skipit
- test_calendarObjectsWithDirectory = skipit
- test_hasCalendarResourceUIDSomewhereElse = skipit
-
-del FileStorageTests
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -40,7 +40,6 @@
from twisted.python import hashlib
from twistedcaldav.config import config
-from twistedcaldav.memcacher import Memcacher
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError, Property, \
vCardProductID
@@ -86,13 +85,12 @@
_notifierPrefix = "CardDAV"
_dataVersionKey = "ADDRESSBOOK-DATAVERSION"
- _cacher = Memcacher("SQL.adbkhome", pickle=True, key_normalization=False)
- def __init__(self, transaction, ownerUID, authzUID=None):
+ def __init__(self, transaction, homeData, authzUID=None):
- super(AddressBookHome, self).__init__(transaction, ownerUID, authzUID=authzUID)
self._addressbookPropertyStoreID = None
+ super(AddressBookHome, self).__init__(transaction, homeData, authzUID=authzUID)
self._addressbook = None
@@ -118,6 +116,7 @@
return (
cls._homeSchema.RESOURCE_ID,
cls._homeSchema.OWNER_UID,
+ cls._homeSchema.STATUS,
cls._homeSchema.ADDRESSBOOK_PROPERTY_STORE_ID,
)
@@ -133,19 +132,20 @@
return (
"_resourceID",
"_ownerUID",
+ "_status",
"_addressbookPropertyStoreID",
)
@inlineCallbacks
- def initFromStore(self, no_cache=False):
+ def initFromStore(self):
"""
Initialize this object from the store. We read in and cache all the
extra meta-data from the DB to avoid having to do DB queries for those
individually later.
"""
- result = yield super(AddressBookHome, self).initFromStore(no_cache)
+ result = yield super(AddressBookHome, self).initFromStore()
if result is not None:
# Created owned address book
addressbook = AddressBook(
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql_external.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql_external.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/sql_external.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -18,8 +18,6 @@
SQL backend for CardDAV storage when resources are external.
"""
-from twisted.internet.defer import succeed
-
from twext.python.log import Logger
from txdav.carddav.datastore.sql import AddressBookHome, AddressBook, \
@@ -31,10 +29,10 @@
class AddressBookHomeExternal(CommonHomeExternal, AddressBookHome):
- def __init__(self, transaction, ownerUID, resourceID):
+ def __init__(self, transaction, homeData):
- AddressBookHome.__init__(self, transaction, ownerUID)
- CommonHomeExternal.__init__(self, transaction, ownerUID, resourceID)
+ AddressBookHome.__init__(self, transaction, homeData)
+ CommonHomeExternal.__init__(self, transaction, homeData)
def hasAddressBookResourceUIDSomewhereElse(self, uid, ok_object, mode):
@@ -51,13 +49,6 @@
raise AssertionError("CommonHomeExternal: not supported")
- def createdHome(self):
- """
- No children - make this a no-op.
- """
- return succeed(None)
-
-
def addressbook(self):
"""
No children.
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -70,7 +70,7 @@
populateTxn = self.storeUnderTest().newTransaction()
for homeUID in self.requirements:
addressbooks = self.requirements[homeUID]
- home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
+ home = yield populateTxn.addressbookHomeWithUID(homeUID, create=True)
if addressbooks is not None:
addressbook = home.addressbook()
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql_sharing.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/carddav/datastore/test/test_sql_sharing.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -45,7 +45,7 @@
for homeUID in self.requirements:
addressbooks = self.requirements[homeUID]
if addressbooks is not None:
- home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
+ home = yield populateTxn.addressbookHomeWithUID(homeUID, create=True)
addressbook = home.addressbook()
addressbookObjNames = addressbooks[addressbook.name()]
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/file.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/file.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -334,15 +334,15 @@
CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = AddressBookHome
- def calendarHomeWithUID(self, uid, create=False):
- return self.homeWithUID(ECALENDARTYPE, uid, create=create)
+ def calendarHomeWithUID(self, uid, status=None, create=False):
+ return self.homeWithUID(ECALENDARTYPE, uid, status=status, create=create)
- def addressbookHomeWithUID(self, uid, create=False):
- return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
+ def addressbookHomeWithUID(self, uid, status=None, create=False):
+ return self.homeWithUID(EADDRESSBOOKTYPE, uid, status=status, create=create)
- def _determineMemo(self, storeType, uid, create=False):
+ def _determineMemo(self, storeType, uid, status=None, create=False):
"""
Determine the memo dictionary to use for homeWithUID.
"""
@@ -365,7 +365,7 @@
@memoizedKey("uid", _determineMemo, deferredResult=False)
- def homeWithUID(self, storeType, uid, create=False):
+ def homeWithUID(self, storeType, uid, status=None, create=False):
if uid.startswith("."):
return None
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-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -25,10 +25,11 @@
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
+from txdav.common.datastore.sql_tables import _HOME_STATUS_EXTERNAL, \
+ _HOME_STATUS_MIGRATING
from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
-import uuid
+from uuid import uuid4
log = Logger()
@@ -100,10 +101,6 @@
return "Cross-pod Migration Sync for {}: {}".format(self.diruid, detail)
- def migratingUid(self):
- return "Migrating-{}".format(self.diruid)
-
-
@inlineCallbacks
def migrateHere(self):
"""
@@ -151,7 +148,7 @@
"""
yield self.loadRecord()
- self.homeId = yield self.prepareCalendarHome()
+ yield self.prepareCalendarHome()
# Calendar list and calendar data
yield self.syncCalendarList()
@@ -249,10 +246,10 @@
Make sure the inactive home to migrate into is present on this pod.
"""
- home = yield txn.calendarHomeWithUID(self.migratingUid())
+ home = yield self._localHome(txn)
if home is None:
- home = yield txn.calendarHomeWithUID(self.migratingUid(), create=True, migratingUID=self.diruid)
- returnValue(home.id())
+ home = yield txn.calendarHomeWithUID(self.diruid, status=_HOME_STATUS_MIGRATING, create=True)
+ self.homeId = home.id()
@inTransactionWrapper
@@ -268,7 +265,7 @@
calendars = yield CalendarMigrationRecord.querysimple(txn, calendarHomeResourceID=self.homeId)
calendarIDMap = dict((item.remoteResourceID, item.localResourceID) for item in calendars)
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
yield local_home.copyMetadata(remote_home, calendarIDMap)
@@ -276,19 +273,22 @@
def _remoteHome(self, txn):
"""
Create a synthetic external home object that maps to the actual remote home.
-
- @param ownerUID: directory uid of the user's home
- @type ownerUID: L{str}
"""
from txdav.caldav.datastore.sql_external import CalendarHomeExternal
resourceID = yield txn.store().conduit.send_home_resource_id(txn, self.record)
- home = CalendarHomeExternal(txn, self.record.uid, resourceID) if resourceID is not None else None
- if home:
- home._childClass = home._childClass._externalClass
+ home = CalendarHomeExternal.makeSyntheticExternalHome(txn, self.record.uid, resourceID) if resourceID is not None else None
returnValue(home)
+ def _localHome(self, txn):
+ """
+ Get the home on this pod that will have data migrated to it.
+ """
+
+ return txn.calendarHomeWithUID(self.diruid, status=_HOME_STATUS_MIGRATING)
+
+
@inlineCallbacks
def syncCalendarList(self):
"""
@@ -377,7 +377,7 @@
@param remote_sync_state: remote sync state
@type remote_sync_state: L{dict}
"""
- home = yield txn.calendarHomeWithUID(self.migratingUid())
+ home = yield self._localHome(txn)
for remoteID in set(local_sync_state.keys()) - set(remote_sync_state.keys()):
calendar = yield home.childWithID(local_sync_state[remoteID].localResourceID)
if calendar is not None:
@@ -431,8 +431,8 @@
of the calendar right now - it will be sync'd later.
"""
- home = yield txn.calendarHomeWithUID(self.migratingUid())
- calendar = yield home.createChildWithName(str(uuid.uuid4()))
+ home = yield self._localHome(txn)
+ calendar = yield home.createChildWithName(str(uuid4()))
returnValue(calendar.id())
@@ -452,7 +452,7 @@
returnValue(None)
# Check whether the deleted set items
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
local_calendar = yield local_home.childWithID(migrationRecord.localResourceID)
yield local_calendar.copyMetadata(remote_calendar)
@@ -478,7 +478,7 @@
changed, deleted, _ignore_invalid = yield remote_calendar.resourceNamesSinceToken(migrationRecord.lastSyncToken)
# Check whether the deleted set items
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
local_calendar = yield local_home.childWithID(migrationRecord.localResourceID)
# Check the md5's on each changed remote with the local one to filter out ones
@@ -531,7 +531,7 @@
"""
# Check whether the deleted set items
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
local_calendar = yield local_home.childWithID(localID)
local_objects = yield local_calendar.objectResourcesWithNames(purge_names)
@@ -587,7 +587,7 @@
remote_objects = dict([(obj.name(), obj) for obj in remote_objects])
# Get local objects
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
local_calendar = yield local_home.childWithID(localID)
local_objects = yield local_calendar.objectResourcesWithNames(remaining)
local_objects = dict([(obj.name(), obj) for obj in local_objects])
@@ -652,7 +652,7 @@
rattachments = yield remote_home.getAllAttachments()
rmap = dict([(attachment.id(), attachment) for attachment in rattachments])
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
lattachments = yield local_home.getAllAttachments()
lmap = dict([(attachment.id(), attachment) for attachment in lattachments])
@@ -706,7 +706,7 @@
"""
remote_home = yield self._remoteHome(txn)
- local_home = yield txn.calendarHomeWithUID(self.migratingUid())
+ local_home = yield self._localHome(txn)
attachment = yield local_home.getAttachmentByID(local_id)
if attachment is None:
returnValue(None)
@@ -951,13 +951,24 @@
"""
Sync all the collections shared by the migrating user from the remote store. We will do this one calendar at a time since
there could be a large number of sharees per calendar.
+
+ Here is the logic we need: first assume we have three pods: A, B, C, and we are migrating a user from A->B. We start
+ with a set of shares (X -> Y - where X is the sharer and Y the sharee) on pod A. We migrate the sharer to pod B. We
+ then need to have a set of bind records on pod B, and adjust the set on pod A. Note that no changes are required on pod C.
+
+ Original | Changes | Changes
+ Shares | on B | on A
+ --------------|------------------------------|---------------------
+ A -> A | B -> A (new) | B -> A (modify existing)
+ A -> B | B -> B (modify existing) | (removed)
+ A -> C | B -> C (new) | (removed)
"""
calendars = yield self.getSyncState()
len_records = 0
for calendar in calendars.values():
- records = yield self.sharedByCollectionRecords(calendar.remoteResourceID)
+ records, bindUID = yield self.sharedByCollectionRecords(calendar.remoteResourceID, calendar.localResourceID)
records = records.items()
# Batch setting resources for the local home
@@ -966,22 +977,36 @@
yield self.makeSharedByCollections(records[:50], calendar.localResourceID)
records = records[50:]
+ # Update the remote pod to switch over the shares
+ yield self.updatedRemoteSharedByCollections(calendar.remoteResourceID, bindUID)
+
returnValue(len_records)
@inTransactionWrapper
@inlineCallbacks
- def sharedByCollectionRecords(self, txn, remote_id):
+ def sharedByCollectionRecords(self, txn, remote_id, local_id):
"""
- Get all the existing L{CalendarBindRecord}'s from the remote store.
+ Get all the existing L{CalendarBindRecord}'s from the remote store. Also make sure a
+ bindUID exists for the local calendar.
"""
remote_home = yield self._remoteHome(txn)
remote_calendar = yield remote_home.childWithID(remote_id)
records = yield remote_calendar.sharingBindRecords()
- returnValue(records)
+ # Check bindUID
+ local_records = yield CalendarBindRecord.querysimple(
+ txn,
+ calendarHomeResourceID=self.homeId,
+ calendarResourceID=local_id,
+ )
+ if not local_records[0].bindUID:
+ yield local_records[0].update(bindUID=str(uuid4()))
+ returnValue((records, local_records[0].bindUID,))
+
+
@inTransactionWrapper
@inlineCallbacks
def makeSharedByCollections(self, txn, records, calendar_id):
@@ -1016,10 +1041,34 @@
yield record.insert(txn)
+ @inTransactionWrapper
@inlineCallbacks
+ def updatedRemoteSharedByCollections(self, txn, remote_id, bindUID):
+ """
+ Get all the existing L{CalendarBindRecord}'s from the remote store.
+ """
+
+ remote_home = yield self._remoteHome(txn)
+ remote_calendar = yield remote_home.childWithID(remote_id)
+ records = yield remote_calendar.migrateBindRecords(bindUID)
+ returnValue(records)
+
+
+ @inlineCallbacks
def sharedToCollectionsReconcile(self):
"""
Sync all the collections shared to the migrating user from the remote store.
+
+ Here is the logic we need: first assume we have three pods: A, B, C, and we are migrating a user from A->B. We start
+ with a set of shares (X -> Y - where X is the sharer and Y the sharee) with sharee on pod A. We migrate the sharee to pod B. We
+ then need to have a set of bind records on pod B, and adjust the set on pod A. Note that no changes are required on pod C.
+
+ Original | Changes | Changes
+ Shares | on B | on A
+ --------------|------------------------------|---------------------
+ A -> A | A -> B (new) | A -> B (modify existing)
+ B -> A | B -> B (modify existing) | (removed)
+ C -> A | C -> B (new) | (removed)
"""
records = yield self.sharedToCollectionRecords()
records = records.items()
@@ -1080,6 +1129,8 @@
# resource ID from that to use in the new CALENDAR_BIND record we create. If a pre-existing share
# is not present, then we have to create the CALENDAR table entry and associated pieces
+ remote_id = shareeRecord.calendarResourceID
+
# Look for pre-existing share with the same external ID
oldrecord = yield CalendarBindRecord.querysimple(
txn,
@@ -1101,3 +1152,18 @@
shareeRecord.calendarResourceID = calendar_id
shareeRecord.bindRevision = 0
yield shareeRecord.insert(txn)
+
+ yield self.updatedRemoteSharedToCollection(remote_id, txn=txn)
+
+
+ @inTransactionWrapper
+ @inlineCallbacks
+ def updatedRemoteSharedToCollection(self, txn, remote_id):
+ """
+ Get all the existing L{CalendarBindRecord}'s from the remote store.
+ """
+
+ remote_home = yield self._remoteHome(txn)
+ remote_calendar = yield remote_home.childWithID(remote_id)
+ records = yield remote_calendar.migrateBindRecords(None)
+ returnValue(records)
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-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/test/test_home_sync.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -31,7 +31,7 @@
ExternalDelegateGroupsRecord, DelegateGroupsRecord
from txdav.common.datastore.sql_notification import NotificationCollection
from txdav.common.datastore.sql_tables import schema, _HOME_STATUS_EXTERNAL, \
- _BIND_MODE_READ
+ _BIND_MODE_READ, _HOME_STATUS_MIGRATING
from txdav.common.datastore.test.util import populateCalendarsFrom
from txdav.who.delegates import Delegates
from txweb2.http_headers import MimeType
@@ -154,14 +154,14 @@
# No home present
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
- home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home is None)
yield self.commitTransaction(1)
yield syncer.prepareCalendarHome()
# Home is present
- home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home is not None)
children = yield home.listChildren()
self.assertEqual(len(children), 0)
@@ -176,7 +176,7 @@
# No home present
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
- home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home is None)
yield self.commitTransaction(1)
@@ -184,7 +184,7 @@
yield self.commitTransaction(1)
# Home is present
- home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home is not None)
children = yield home.listChildren()
self.assertEqual(len(children), 0)
@@ -257,7 +257,7 @@
yield syncer.sync()
# Home is present with correct metadata
- home1 = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home1 is not None)
calendar1 = yield home1.childWithName("calendar")
events1 = yield home1.childWithName("events")
@@ -284,7 +284,7 @@
yield syncer.sync()
# Home is present with correct metadata
- home1 = yield self.homeUnderTest(self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
self.assertTrue(home1 is not None)
calendar1 = yield home1.childWithName("calendar")
events1 = yield home1.childWithName("events")
@@ -345,10 +345,10 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
# No local calendar exists yet
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
children = yield home1.listChildren()
self.assertEqual(len(children), 0)
yield self.commitTransaction(1)
@@ -370,7 +370,7 @@
self.assertEqual(local_sync_state[remote_id].lastSyncToken, remote_sync_state[remote_id].lastSyncToken)
# Local calendar exists
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is not None)
yield self.commitTransaction(1)
@@ -395,10 +395,10 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
# No local calendar exists yet
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is None)
yield self.commitTransaction(1)
@@ -427,7 +427,7 @@
# Local calendar exists
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is not None)
children = yield calendar1.objectResources()
@@ -451,7 +451,7 @@
)
object1 = yield self.calendarObjectUnderTest(
- txn=self.theTransactionUnderTest(1), home=syncer.migratingUid(), calendar_name="calendar", name="1.ics"
+ txn=self.theTransactionUnderTest(1), home="user01", status=_HOME_STATUS_MIGRATING, calendar_name="calendar", name="1.ics"
)
caldata = yield object1.component()
self.assertEqual(normalize_iCalStr(caldata), normalize_iCalStr(self.caldata1_changed))
@@ -472,7 +472,7 @@
remote_sync_state,
)
- calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home=syncer.migratingUid(), name="calendar")
+ calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="user01", status=_HOME_STATUS_MIGRATING, name="calendar")
children = yield calendar1.objectResources()
self.assertEqual(set([child.name() for child in children]), set(("1.ics", "3.ics",)))
mapping1 = dict([(o.name(), o.id()) for o in children])
@@ -492,7 +492,7 @@
remote_sync_state,
)
- calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home=syncer.migratingUid(), name="calendar")
+ calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="user01", status=_HOME_STATUS_MIGRATING, name="calendar")
children = yield calendar1.objectResources()
self.assertEqual(set([child.name() for child in children]), set(("1.ics", "3.ics", "4.ics")))
mapping1 = dict([(o.name(), o.id()) for o in children])
@@ -515,17 +515,17 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
# No local calendar exists yet
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
children1 = yield home1.loadChildren()
self.assertEqual(len(children1), 0)
yield self.commitTransaction(1)
# Trigger sync
yield syncer.syncCalendarList()
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
children1 = yield home1.loadChildren()
details1 = dict([(child.id(), child.name()) for child in children1])
self.assertEqual(set(details1.values()), set(details0.values()))
@@ -539,7 +539,7 @@
# Trigger sync
yield syncer.syncCalendarList()
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
children1 = yield home1.loadChildren()
details1 = dict([(child.id(), child.name()) for child in children1])
self.assertTrue("new-calendar" in details1.values())
@@ -555,7 +555,7 @@
# Trigger sync
yield syncer.syncCalendarList()
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
children1 = yield home1.loadChildren()
details1 = dict([(child.id(), child.name()) for child in children1])
self.assertTrue("new-calendar" not in details1.values())
@@ -582,7 +582,7 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
# Trigger sync of the one calendar
local_sync_state = {}
@@ -623,7 +623,7 @@
# Local calendar exists
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is not None)
children = yield calendar1.objectResources()
@@ -649,7 +649,7 @@
self.assertEqual(removed, set())
# Validate changes
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
attachments = yield home1.getAllAttachments()
mapping1 = dict([(o.md5(), o.id()) for o in attachments])
yield _checkAttachmentObjectMigrationState(home1, mapping1)
@@ -668,7 +668,7 @@
self.assertEqual(removed, set())
# Validate changes
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
attachments = yield home1.getAllAttachments()
mapping1 = dict([(o.md5(), o.id()) for o in attachments])
yield _checkAttachmentObjectMigrationState(home1, mapping1)
@@ -689,7 +689,7 @@
self.assertEqual(removed, set((id0_1,)))
# Validate changes
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
attachments = yield home1.getAllAttachments()
mapping1 = dict([(o.md5(), o.id()) for o in attachments])
yield _checkAttachmentObjectMigrationState(home1, mapping1)
@@ -711,7 +711,7 @@
self.assertEqual(removed, set())
# Validate changes
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
attachments = yield home1.getAllAttachments()
mapping1 = dict([(o.md5(), o.id()) for o in attachments])
yield _checkAttachmentObjectMigrationState(home1, mapping1)
@@ -730,7 +730,7 @@
self.assertEqual(removed, set())
# Validate changes
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
attachments = yield home1.getAllAttachments()
mapping1 = dict([(o.md5(), o.id()) for o in attachments])
yield _checkAttachmentObjectMigrationState(home1, mapping1)
@@ -776,7 +776,7 @@
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
# Trigger sync of the one calendar
local_sync_state = {}
@@ -810,7 +810,7 @@
self.assertEqual(len_links, 3)
# Local calendar exists
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is not None)
children = yield calendar1.objectResources()
@@ -950,7 +950,7 @@
# Sync from remote side
syncer = CrossPodHomeSync(self.theStoreUnderTest(1), "user01")
yield syncer.loadRecord()
- syncer.homeId = yield syncer.prepareCalendarHome()
+ yield syncer.prepareCalendarHome()
changes = yield syncer.notificationsReconcile()
self.assertEqual(changes, 2)
@@ -1030,28 +1030,50 @@
self.assertEqual(changes, 2)
# Local calendar exists with shares
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
invites1 = yield calendar1.sharingInvites()
self.assertEqual(len(invites1), 2)
self.assertEqual(set([invite.uid for invite in invites1]), set((shared_name_02, shared_name_03,)))
yield self.commitTransaction(1)
+ # Remote sharee can access it
+ home0 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user02")
+ calendar0 = yield home0.childWithName(shared_name_02)
+ self.assertTrue(calendar0 is not None)
+
# Local sharee can access it
home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser03")
calendar1 = yield home1.childWithName(shared_name_03)
self.assertTrue(calendar1 is not None)
# Local shared calendars exist
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName(shared_name_04)
self.assertTrue(calendar1 is not None)
calendar1 = yield home1.childWithName(shared_name_05)
self.assertTrue(calendar1 is not None)
yield self.commitTransaction(1)
+ # Sharers see migrated user as sharee
+ externalHome0 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", status=_HOME_STATUS_EXTERNAL)
+ calendar0 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user04", name="calendar")
+ invites = yield calendar0.allInvitations()
+ self.assertEqual(len(invites), 1)
+ self.assertEqual(invites[0].shareeUID, "user01")
+ self.assertEqual(invites[0].shareeHomeID, externalHome0.id())
+ yield self.commitTransaction(0)
+ shareeHome1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
+ calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(1), home="puser05", name="calendar")
+ invites = yield calendar1.allInvitations()
+ self.assertEqual(len(invites), 1)
+ self.assertEqual(invites[0].shareeUID, "user01")
+ self.assertEqual(invites[0].shareeHomeID, shareeHome1.id())
+ yield self.commitTransaction(1)
+
+
class TestGroupAttendeeSync(MultiStoreConduitTest):
"""
GroupAttendeeReconciliation tests
@@ -1160,7 +1182,7 @@
self.assertEqual(len_links, 2)
# Local calendar exists
- home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name=syncer.migratingUid())
+ home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="user01", status=_HOME_STATUS_MIGRATING)
calendar1 = yield home1.childWithName("calendar")
self.assertTrue(calendar1 is not None)
children = yield calendar1.objectResources()
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-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -168,6 +168,7 @@
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_resourcenamessincerevision", "resourceNamesSinceRevision", transform_send_result=UtilityConduitMixin._to_tuple)
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_search", "search")
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_sharing_records", "sharingBindRecords", transform_recv_result=StoreAPIConduitMixin._to_serialize_dict_value)
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_migrate_sharing_records", "migrateBindRecords")
# Calls on L{CommonObjectResource} objects
UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_loadallobjects", "loadAllObjects", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize_list)
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -105,7 +105,7 @@
from txdav.caldav.datastore.sql_external import CalendarHomeExternal
recipient = yield txn.store().directoryService().recordWithUID(uid)
resourceID = yield txn.store().conduit.send_home_resource_id(txn, recipient)
- home = CalendarHomeExternal(txn, recipient.uid, resourceID) if resourceID is not None else None
+ home = CalendarHomeExternal.makeSyntheticExternalHome(txn, recipient.uid, resourceID) if resourceID is not None else None
if home:
home._childClass = home._childClass._externalClass
returnValue(home)
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-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -14,9 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from txdav.common.datastore.sql_notification import NotificationCollection
-from txdav.common.datastore.sql_util import _EmptyCacher, _SharedSyncLogic
-from txdav.common.datastore.sql_sharing import SharingHomeMixIn, SharingMixIn
"""
SQL data store.
@@ -64,9 +61,12 @@
from txdav.common.datastore.sql_directory import DelegatesAPIMixin, \
GroupsAPIMixin, GroupCacherAPIMixin
from txdav.common.datastore.sql_imip import imipAPIMixin
+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
+from txdav.common.datastore.sql_util import _SharedSyncLogic
+from txdav.common.datastore.sql_sharing import SharingHomeMixIn, SharingMixIn
from txdav.common.icommondatastore import ConcurrentModification, \
RecordNotAllowedError, ShareNotAllowed, \
IndexedSearchException, EADDRESSBOOKTYPE, ECALENDARTYPE
@@ -81,6 +81,7 @@
from zope.interface import implements, directlyProvides
+import collections
import inspect
import itertools
import sys
@@ -567,8 +568,16 @@
self._store = store
self._queuer = self._store.queuer
- self._calendarHomes = {}
- self._addressbookHomes = {}
+ self._cachedHomes = {
+ ECALENDARTYPE: {
+ "byUID": collections.defaultdict(dict),
+ "byID": collections.defaultdict(dict),
+ },
+ EADDRESSBOOKTYPE: {
+ "byUID": collections.defaultdict(dict),
+ "byID": collections.defaultdict(dict),
+ },
+ }
self._notificationHomes = {}
self._notifierFactories = notifierFactories
self._notifiedAlready = set()
@@ -677,14 +686,11 @@
).on(self)
- def _determineMemo(self, storeType, uid, create=False, authzUID=None, migratingUID=None):
+ def _determineMemo(self, storeType, lookupMode, status):
"""
Determine the memo dictionary to use for homeWithUID.
"""
- if storeType == ECALENDARTYPE:
- return self._calendarHomes
- else:
- return self._addressbookHomes
+ return self._cachedHomes[storeType][lookupMode][status]
@inlineCallbacks
@@ -699,11 +705,11 @@
yield self.homeWithUID(storeType, uid, create=False)
# Return the memoized list directly
- returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, None).items(), key=lambda x: x[0])])
+ returnValue([kv[1] for kv in sorted(self._determineMemo(storeType, "byUID", _HOME_STATUS_NORMAL).items(), key=lambda x: x[0])])
- @memoizedKey("uid", _determineMemo)
- def homeWithUID(self, storeType, uid, create=False, authzUID=None, migratingUID=None):
+ @inlineCallbacks
+ def homeWithUID(self, storeType, uid, status=None, create=False, authzUID=None):
"""
We need to distinguish between various different users "looking" at a home and its
child resources because we have per-user properties that depend on which user is "looking".
@@ -715,15 +721,21 @@
if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
raise RuntimeError("Unknown home type.")
- return self._homeClass[storeType].homeWithUID(self, uid, create, authzUID, migratingUID)
+ result = self._determineMemo(storeType, "byUID", status).get(uid)
+ if result is None:
+ result = yield self._homeClass[storeType].homeWithUID(self, uid, status, create, authzUID)
+ if result:
+ self._determineMemo(storeType, "byUID", status)[uid] = result
+ self._determineMemo(storeType, "byID", None)[result.id()] = result
+ returnValue(result)
- def calendarHomeWithUID(self, uid, create=False, authzUID=None, migratingUID=None):
- return self.homeWithUID(ECALENDARTYPE, uid, create=create, authzUID=authzUID, migratingUID=migratingUID)
+ def calendarHomeWithUID(self, uid, status=None, create=False, authzUID=None):
+ return self.homeWithUID(ECALENDARTYPE, uid, status=status, create=create, authzUID=authzUID)
- def addressbookHomeWithUID(self, uid, create=False, authzUID=None, migratingUID=None):
- return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create, authzUID=authzUID, migratingUID=migratingUID)
+ def addressbookHomeWithUID(self, uid, status=None, create=False, authzUID=None):
+ return self.homeWithUID(EADDRESSBOOKTYPE, uid, status=status, create=create, authzUID=authzUID)
@inlineCallbacks
@@ -731,12 +743,15 @@
"""
Load a calendar or addressbook home by its integer resource ID.
"""
- uid = (yield self._homeClass[storeType].homeUIDWithResourceID(self, rid))
- if uid:
- # Always get the owner's view of the home = i.e., authzUID=uid
- result = (yield self.homeWithUID(storeType, uid, authzUID=uid))
- else:
- result = None
+ if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
+ raise RuntimeError("Unknown home type.")
+
+ result = self._determineMemo(storeType, "byID", None).get(rid)
+ if result is None:
+ result = yield self._homeClass[storeType].homeWithResourceID(self, rid)
+ if result:
+ self._determineMemo(storeType, "byID", None)[rid] = result
+ self._determineMemo(storeType, "byUID", result._status)[result.uid()] = result
returnValue(result)
@@ -1530,38 +1545,38 @@
_dataVersionKey = None
_dataVersionValue = None
- _cacher = None # Initialize in derived classes
-
@classmethod
- @inlineCallbacks
- def makeClass(cls, transaction, ownerUID, no_cache=False, authzUID=None):
+ def makeClass(cls, transaction, homeData, authzUID=None):
"""
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 ownerUID: owner UID of home to load
- @type ownerUID: C{str}
- @param no_cache: should cached query be used
- @type no_cache: C{bool}
+ @param homeData: home table column data
+ @type homeData: C{list}
"""
- home = cls(transaction, ownerUID, authzUID=authzUID)
- actualHome = yield home.initFromStore(no_cache)
- returnValue(actualHome)
+ status = homeData[cls.homeColumns().index(cls._homeSchema.STATUS)]
+ if status == _HOME_STATUS_EXTERNAL:
+ home = cls._externalClass(transaction, homeData)
+ else:
+ home = cls(transaction, homeData, authzUID=authzUID)
+ return home.initFromStore()
- def __init__(self, transaction, ownerUID, authzUID=None):
+
+ def __init__(self, transaction, homeData, authzUID=None):
self._txn = transaction
- self._ownerUID = ownerUID
+
+ for attr, value in zip(self.homeAttributes(), homeData):
+ setattr(self, attr, value)
+
self._authzUID = authzUID
if self._authzUID is None:
if self._txn._authz_uid is not None:
self._authzUID = self._txn._authz_uid
else:
self._authzUID = self._ownerUID
- self._resourceID = None
- self._status = _HOME_STATUS_NORMAL
self._dataVersion = None
self._childrenLoaded = False
self._children = {}
@@ -1570,15 +1585,13 @@
self._created = None
self._modified = None
self._syncTokenRevision = None
- if transaction._disableCache:
- self._cacher = _EmptyCacher()
# This is used to track whether the originating request is from the store associated
# by the transaction, or from a remote store. We need to be able to distinguish store
# objects that are locally hosted (_HOME_STATUS_NORMAL) or remotely hosted
# (_HOME_STATUS_EXTERNAL). For the later we need to know whether the object is being
# accessed from the local store (in which case requests for child objects etc will be
- # directed at a remote store) or whether it is being accessed as the tresult of a remote
+ # directed at a remote store) or whether it is being accessed as the result of a remote
# request (in which case requests for child objects etc will be directed at the local store).
self._internalRequest = True
@@ -1603,14 +1616,16 @@
return Select(
cls.homeColumns(),
From=home,
- Where=home.OWNER_UID == Parameter("ownerUID")
+ Where=(home.OWNER_UID == Parameter("ownerUID")).And(
+ home.STATUS == Parameter("status")
+ )
)
@classproperty
def _ownerFromResourceID(cls):
home = cls._homeSchema
- return Select([home.OWNER_UID],
+ return Select([home.OWNER_UID, home.STATUS],
From=home,
Where=home.RESOURCE_ID == Parameter("resourceID"))
@@ -1686,41 +1701,22 @@
@inlineCallbacks
- def initFromStore(self, no_cache=False):
+ def initFromStore(self):
"""
Initialize this object from the store. We read in and cache all the
extra meta-data from the DB to avoid having to do DB queries for those
individually later.
"""
- result = yield self._cacher.get(self._ownerUID)
- if result is None:
- result = yield self._homeColumnsFromOwnerQuery.on(self._txn, ownerUID=self._ownerUID)
- if result:
- result = result[0]
- if not no_cache:
- yield self._cacher.set(self._ownerUID, result)
- if result:
- for attr, value in zip(self.homeAttributes(), result):
- setattr(self, attr, value)
+ yield self.initMetaDataFromStore()
+ yield self._loadPropertyStore()
- # STOP! 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 self._status == _HOME_STATUS_EXTERNAL:
- actualHome = self._externalClass(self._txn, self._ownerUID, self._resourceID)
- else:
- actualHome = self
- yield actualHome.initMetaDataFromStore()
- yield actualHome._loadPropertyStore()
+ for factory_type, factory in self._txn._notifierFactories.items():
+ self.addNotifier(factory_type, factory.newNotifier(self))
- for factory_type, factory in self._txn._notifierFactories.items():
- actualHome.addNotifier(factory_type, factory.newNotifier(actualHome))
+ returnValue(self)
- returnValue(actualHome)
- else:
- returnValue(None)
-
@inlineCallbacks
def initMetaDataFromStore(self):
"""
@@ -1780,31 +1776,108 @@
@classmethod
+ def homeWithUID(cls, txn, uid, status=None, create=False, authzUID=None):
+ return cls.homeWith(txn, None, uid, status, create=create, authzUID=authzUID)
+
+
+ @classmethod
+ def homeWithResourceID(cls, txn, rid):
+ return cls.homeWith(txn, rid, None)
+
+
+ @classmethod
@inlineCallbacks
- def homeWithUID(cls, txn, uid, create=False, authzUID=None, migratingUID=None):
+ def homeWith(cls, txn, rid, uid, status=None, create=False, authzUID=None):
"""
- @param uid: I'm going to assume uid is utf-8 encoded bytes
+ Lookup or create a home based in either its resource id or uid. If a status is given,
+ return only the one matching that status. If status is L{None} we lookup any regular
+ status type (normal, external or purging). When creating with status L{None} we create
+ one with a status matching the current directory record thisServer() value. The only
+ other status that can be directly created is migrating.
"""
- homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
- if homeObject is not None:
+
+ # Setup the SQL query and query cacher keys
+ queryCacher = txn._queryCacher
+ cacheKeys = []
+ if rid is not None:
+ query = cls._homeSchema.RESOURCE_ID == rid
+ if queryCacher:
+ cacheKeys.append(queryCacher.keyForHomeWithID(cls._homeType, rid, status))
+ elif uid is not None:
+ query = cls._homeSchema.OWNER_UID == uid
+ if status is not None:
+ query = query.And(cls._homeSchema.STATUS == status)
+ if queryCacher:
+ cacheKeys.append(queryCacher.keyForHomeWithUID(cls._homeType, uid, status))
+ else:
+ statusSet = (_HOME_STATUS_NORMAL, _HOME_STATUS_EXTERNAL, _HOME_STATUS_PURGING)
+ query = query.And(cls._homeSchema.STATUS.In(statusSet))
+ if queryCacher:
+ for item in statusSet:
+ cacheKeys.append(queryCacher.keyForHomeWithUID(cls._homeType, uid, item))
+ else:
+ raise AssertionError("One of rid or uid must be set")
+
+ # Try to fetch a result from the query cache first
+ for cacheKey in cacheKeys:
+ result = (yield queryCacher.get(cacheKey))
+ if result is not None:
+ break
+ else:
+ result = None
+
+ # If nothing in thr cache, do the SQL query and cache the result
+ if result is None:
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ 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]
+ elif results:
+ result = results[0]
+ else:
+ result = None
+
+ if result and queryCacher:
+ if rid is not None:
+ cacheKey = cacheKeys[0]
+ elif uid is not None:
+ cacheKey = queryCacher.keyForHomeWithUID(cls._homeType, uid, result[cls.homeColumns().index(cls._homeSchema.STATUS)])
+ yield queryCacher.set(cacheKey, result)
+
+ if result:
+ # Return object that already exists in the store
+ homeObject = yield cls.makeClass(txn, result, authzUID=authzUID)
returnValue(homeObject)
else:
- if not create:
+ # Can only create when uid is specified
+ if not create or uid is None:
returnValue(None)
# Determine if the user is local or external
- diruid = uid if migratingUID is None else migratingUID
- record = yield txn.directoryService().recordWithUID(diruid.decode("utf-8"))
+ 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(diruid))
+ raise DirectoryRecordNotFoundError("Cannot create home for UID since no directory record exists: {}".format(uid))
- if migratingUID is None:
- state = _HOME_STATUS_NORMAL if record.thisServer() else _HOME_STATUS_EXTERNAL
- else:
+ 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")
- state = _HOME_STATUS_MIGRATING
+ 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
savepoint = SavepointAction("homeWithUID")
@@ -1816,7 +1889,7 @@
resourceid = (yield Insert(
{
cls._homeSchema.OWNER_UID: uid,
- cls._homeSchema.STATUS: state,
+ cls._homeSchema.STATUS: createStatus,
cls._homeSchema.DATAVERSION: cls._dataVersionValue,
},
Return=cls._homeSchema.RESOURCE_ID
@@ -1826,8 +1899,13 @@
yield savepoint.rollback(txn)
# Retry the query - row may exist now, if not re-raise
- homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
- if homeObject:
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ Where=query,
+ ).on(txn)
+ if results:
+ homeObject = yield cls.makeClass(txn, results[0], authzUID=authzUID)
returnValue(homeObject)
else:
raise
@@ -1835,28 +1913,27 @@
yield savepoint.release(txn)
# Note that we must not cache the owner_uid->resource_id
- # mapping in _cacher when creating as we don't want that to appear
+ # mapping in the query cacher when creating as we don't want that to appear
# until AFTER the commit
- home = yield cls.makeClass(txn, uid, no_cache=True, authzUID=authzUID)
- if migratingUID is None:
- yield home.createdHome()
- returnValue(home)
+ results = yield Select(
+ cls.homeColumns(),
+ From=cls._homeSchema,
+ Where=cls._homeSchema.RESOURCE_ID == resourceid,
+ ).on(txn)
+ homeObject = yield cls.makeClass(txn, results[0], authzUID=authzUID)
+ if homeObject.normal():
+ yield homeObject.createdHome()
+ returnValue(homeObject)
- @classmethod
- @inlineCallbacks
- def homeUIDWithResourceID(cls, txn, rid):
- rows = (yield cls._ownerFromResourceID.on(txn, resourceID=rid))
- if rows:
- returnValue(rows[0][0])
- else:
- returnValue(None)
-
-
def __repr__(self):
return "<%s: %s, %s>" % (self.__class__.__name__, self._resourceID, self._ownerUID)
+ def cacheKey(self):
+ return "{}-{}".format(self._status, self._ownerUID)
+
+
def id(self):
"""
Retrieve the store identifier for this home.
@@ -1957,8 +2034,18 @@
{self._homeSchema.STATUS: newStatus},
Where=(self._homeSchema.RESOURCE_ID == self._resourceID),
).on(self._txn)
+ if self._txn._queryCacher:
+ yield self._txn._queryCacher.delete(self._txn._queryCacher.keyForHomeWithUID(
+ self._homeType,
+ self.uid(),
+ self._status,
+ ))
+ yield self._txn._queryCacher.delete(self._txn._queryCacher.keyForHomeWithID(
+ self._homeType,
+ self.id(),
+ self._status,
+ ))
self._status = newStatus
- yield self._cacher.delete(self._ownerUID)
@inlineCallbacks
@@ -1982,7 +2069,17 @@
yield self.properties()._removeResource()
- yield self._cacher.delete(str(self._ownerUID))
+ if self._txn._queryCacher:
+ yield self._txn._queryCacher.delete(self._txn._queryCacher.keyForHomeWithUID(
+ self._homeType,
+ self.uid(),
+ self._status,
+ ))
+ yield self._txn._queryCacher.delete(self._txn._queryCacher.keyForHomeWithID(
+ self._homeType,
+ self.id(),
+ self._status,
+ ))
@inlineCallbacks
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-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -42,17 +42,38 @@
are all stubbed out since no data for the user is actually hosted in this store.
"""
- def __init__(self, transaction, ownerUID, resourceID):
- super(CommonHomeExternal, self).__init__(transaction, ownerUID)
- self._resourceID = resourceID
- self._status = _HOME_STATUS_EXTERNAL
+ @classmethod
+ def makeSyntheticExternalHome(cls, transaction, diruid, resourceID):
+ """
+ During migration we need to refer to the remote home as an external home but without have a local representation
+ of it in the store. There will be a new local store home for the migrating user that will operate on local store
+ objects. The synthetic home operates only on remote objects.
+ @param diruid: directory UID of user
+ @type diruid: L{str}
+ @param resourceID: resource ID in the remote store
+ @type resourceID: L{int}
+ """
+ attrMap = {
+ "_resourceID": resourceID,
+ "_ownerUID": diruid,
+ "_status": _HOME_STATUS_EXTERNAL,
+ }
+ homeData = [attrMap.get(attr) for attr in cls.homeAttributes()]
+ result = cls(transaction, homeData)
+ result._childClass = result._childClass._externalClass
+ return result
- def initFromStore(self, no_cache=False):
+
+ def __init__(self, transaction, homeData):
+ super(CommonHomeExternal, self).__init__(transaction, homeData)
+
+
+ def initFromStore(self):
"""
- Never called - this should be done by CommonHome.initFromStore only.
+ NoOp for an external share as there is no metadata or properties.
"""
- raise AssertionError("CommonHomeExternal: not supported")
+ return succeed(self)
@inlineCallbacks
@@ -363,7 +384,11 @@
returnValue(dict([(k, self._bindRecordClass.deserialize(v),) for k, v in results.items()]))
+ def migrateBindRecords(self, bindUID):
+ return self._txn.store().conduit.send_homechild_migrate_sharing_records(self, bindUID)
+
+
class CommonObjectResourceExternal(CommonObjectResource):
"""
A CommonObjectResource for a resource not hosted on this system, but on another pod. This will forward
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2015-02-27 19:59:39 UTC (rev 14481)
@@ -29,9 +29,10 @@
create table CALENDAR_HOME (
"RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique,
+ "OWNER_UID" nvarchar2(255),
"STATUS" integer default 0 not null,
- "DATAVERSION" integer default 0 not null
+ "DATAVERSION" integer default 0 not null,
+ unique ("OWNER_UID", "STATUS")
);
create table HOME_STATUS (
@@ -43,6 +44,7 @@
insert into HOME_STATUS (DESCRIPTION, ID) values ('external', 1);
insert into HOME_STATUS (DESCRIPTION, ID) values ('purging', 2);
insert into HOME_STATUS (DESCRIPTION, ID) values ('migrating', 3);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('disabled', 4);
create table CALENDAR (
"RESOURCE_ID" integer primary key
);
@@ -79,9 +81,10 @@
create table NOTIFICATION_HOME (
"RESOURCE_ID" integer primary key,
- "OWNER_UID" nvarchar2(255) unique,
+ "OWNER_UID" nvarchar2(255),
"STATUS" integer default 0 not null,
- "DATAVERSION" integer default 0 not null
+ "DATAVERSION" integer default 0 not null,
+ unique ("OWNER_UID", "STATUS")
);
create table NOTIFICATION (
@@ -262,9 +265,10 @@
create table ADDRESSBOOK_HOME (
"RESOURCE_ID" integer primary key,
"ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
- "OWNER_UID" nvarchar2(255) unique,
+ "OWNER_UID" nvarchar2(255),
"STATUS" integer default 0 not null,
- "DATAVERSION" integer default 0 not null
+ "DATAVERSION" integer default 0 not null,
+ unique ("OWNER_UID", "STATUS")
);
create table ADDRESSBOOK_HOME_METADATA (
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current.sql 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/current.sql 2015-02-27 19:59:39 UTC (rev 14481)
@@ -70,9 +70,11 @@
create table CALENDAR_HOME (
RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
+ OWNER_UID varchar(255) not null, -- implicit index
STATUS integer default 0 not null, -- enum HOME_STATUS
- DATAVERSION integer default 0 not null
+ DATAVERSION integer default 0 not null,
+
+ unique (OWNER_UID, STATUS) -- implicit index
);
-- Enumeration of statuses
@@ -86,6 +88,7 @@
insert into HOME_STATUS values (1, 'external');
insert into HOME_STATUS values (2, 'purging');
insert into HOME_STATUS values (3, 'migrating');
+insert into HOME_STATUS values (4, 'disabled');
--------------
@@ -159,9 +162,11 @@
create table NOTIFICATION_HOME (
RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
+ OWNER_UID varchar(255) not null, -- implicit index
STATUS integer default 0 not null, -- enum HOME_STATUS
- DATAVERSION integer default 0 not null
+ DATAVERSION integer default 0 not null,
+
+ unique (OWNER_UID, STATUS) -- implicit index
);
create table NOTIFICATION (
@@ -475,9 +480,11 @@
create table ADDRESSBOOK_HOME (
RESOURCE_ID integer primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
ADDRESSBOOK_PROPERTY_STORE_ID integer default nextval('RESOURCE_ID_SEQ') not null, -- implicit index
- OWNER_UID varchar(255) not null unique, -- implicit index
+ OWNER_UID varchar(255) not null,
STATUS integer default 0 not null, -- enum HOME_STATUS
- DATAVERSION integer default 0 not null
+ DATAVERSION integer default 0 not null,
+
+ unique (OWNER_UID, STATUS) -- implicit index
);
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_51_to_52.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_51_to_52.sql 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_51_to_52.sql 2015-02-27 19:59:39 UTC (rev 14481)
@@ -20,7 +20,24 @@
-- New status value
insert into HOME_STATUS (DESCRIPTION, ID) values ('migrating', 3);
+insert into HOME_STATUS (DESCRIPTION, ID) values ('disabled', 4);
+-- Home constraints
+alter table CALENDAR_HOME
+ drop unique (OWNER_UID);
+alter table CALENDAR_HOME
+ add unique (OWNER_UID, STATUS);
+
+alter table ADDRESSBOOK_HOME
+ drop unique (OWNER_UID);
+alter table ADDRESSBOOK_HOME
+ add unique (OWNER_UID, STATUS);
+
+alter table NOTIFICATION_HOME
+ drop unique (OWNER_UID);
+alter table NOTIFICATION_HOME
+ add unique (OWNER_UID, STATUS);
+
-- Change columns
alter table CALENDAR_BIND
drop column EXTERNAL_ID
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_51_to_52.sql
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_51_to_52.sql 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_51_to_52.sql 2015-02-27 19:59:39 UTC (rev 14481)
@@ -20,7 +20,21 @@
-- New status value
insert into HOME_STATUS values (3, 'migrating');
+insert into HOME_STATUS values (4, 'disabled');
+-- Home constraints
+alter table CALENDAR_HOME
+ drop constraint CALENDAR_HOME_OWNER_UID_KEY,
+ add unique (OWNER_UID, STATUS);
+
+alter table ADDRESSBOOK_HOME
+ drop constraint ADDRESSBOOK_HOME_OWNER_UID_KEY,
+ add unique (OWNER_UID, STATUS);
+
+alter table NOTIFICATION_HOME
+ drop constraint NOTIFICATION_HOME_OWNER_UID_KEY,
+ add unique (OWNER_UID, STATUS);
+
-- Change columns
alter table CALENDAR_BIND
drop column EXTERNAL_ID,
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_sharing.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_sharing.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -28,7 +28,8 @@
from txdav.base.propertystore.base import PropertyName
from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, _BIND_MODE_DIRECT, \
_BIND_MODE_INDIRECT, _BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED, \
- _BIND_STATUS_INVITED, _BIND_STATUS_INVALID, _BIND_STATUS_DELETED
+ _BIND_STATUS_INVITED, _BIND_STATUS_INVALID, _BIND_STATUS_DELETED, \
+ _HOME_STATUS_EXTERNAL
from txdav.common.icommondatastore import ExternalShareFailed, \
HomeChildNameAlreadyExistsError, AllRetriesFailed
from txdav.xml import element
@@ -87,7 +88,7 @@
# Get the owner home - create external one if not present
ownerHome = yield self._txn.homeWithUID(
- self._homeType, ownerUID, create=True
+ self._homeType, ownerUID, status=_HOME_STATUS_EXTERNAL, create=True
)
if ownerHome is None or not ownerHome.external():
raise ExternalShareFailed("Invalid owner UID: {}".format(ownerUID))
@@ -117,7 +118,7 @@
"""
# Get the owner home
- ownerHome = yield self._txn.homeWithUID(self._homeType, ownerUID)
+ ownerHome = yield self._txn.homeWithUID(self._homeType, ownerUID, status=_HOME_STATUS_EXTERNAL)
if ownerHome is None or not ownerHome.external():
raise ExternalShareFailed("Invalid owner UID: {}".format(ownerUID))
@@ -147,7 +148,7 @@
# Make sure the shareeUID and shareUID match
# Get the owner home - create external one if not present
- shareeHome = yield self._txn.homeWithUID(self._homeType, shareeUID)
+ shareeHome = yield self._txn.homeWithUID(self._homeType, shareeUID, status=_HOME_STATUS_EXTERNAL)
if shareeHome is None or not shareeHome.external():
raise ExternalShareFailed(
"Invalid sharee UID: {}".format(shareeUID)
@@ -761,7 +762,7 @@
@return: the name of the shared calendar in the new calendar home.
@rtype: L{str}
"""
- shareeHome = yield self._txn.calendarHomeWithUID(shareeUID, create=True)
+ shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID, create=True)
returnValue(
(yield self.shareWith(shareeHome, mode, status, summary, shareName))
)
@@ -1041,7 +1042,112 @@
returnValue(dict([(homeMap[getattr(record, self._bindHomeIDAttributeName)], record,) for record in records if record.bindMode != _BIND_MODE_OWN]))
+ def migrateBindRecords(self, bindUID):
+ """
+ The user that owns this collection is being migrated to another pod. We need to switch over
+ the sharing details to point to the new external user.
+ """
+ if self.owned():
+ return self.migrateSharedByRecords(bindUID)
+ else:
+ return self.migrateSharedToRecords()
+
+
@inlineCallbacks
+ def migrateSharedByRecords(self, bindUID):
+ """
+ The user that owns this collection is being migrated to another pod. We need to switch over
+ the sharing details to point to the new external user. For sharees hosted on this pod, we
+ update their bind record to point to a new external home/calendar for the sharer. For sharees
+ hosted on other pods, we simply remove their bind entries.
+ """
+
+ # Get the external home and make sure there is a "fake" calendar associated with it
+ home = yield self.externalHome()
+ calendar = yield home.childWithBindUID(bindUID)
+ if calendar is None:
+ calendar = yield home.createCollectionForExternalShare(
+ self.name(),
+ bindUID,
+ self.getSupportedComponents() if hasattr(self, "getSupportedComponents") else None,
+ )
+
+ remaining = False
+ records = yield self._bindRecordClass.querysimple(self._txn, **{self._bindResourceIDAttributeName: self.id()})
+ for record in records:
+ if record.bindMode == _BIND_MODE_OWN:
+ continue
+ shareeHome = yield self._txn.homeWithResourceID(home._homeType, getattr(record, self._bindHomeIDAttributeName))
+ if shareeHome.normal():
+ remaining = True
+ yield record.update(**{
+ self._bindResourceIDAttributeName: calendar.id(),
+ })
+ else:
+ # It is OK to just delete (as opposed to doing a full "unshare") without adjusting other things
+ # like sync revisions since those would not have been used for an external share anyway. Also,
+ # revisions are tied to the calendar id and the original calendar will be removed after migration
+ # is complete.
+ yield record.delete()
+
+ # If there are no external shares remaining, we can remove the external calendar
+ if not remaining:
+ yield calendar.remove()
+
+
+ @inlineCallbacks
+ def migrateSharedToRecords(self):
+ """
+ The user that owns this collection is being migrated to another pod. We need to switch over
+ the sharing details to point to the new external user.
+ """
+
+ # Update the bind record for this calendar to point to the external home
+ records = yield self._bindRecordClass.querysimple(
+ self._txn,
+ **{
+ self._bindHomeIDAttributeName: self.viewerHome().id(),
+ self._bindResourceIDAttributeName: self.id(),
+ }
+ )
+
+ if len(records) == 1:
+
+ # What we do depends on whether the sharer is local to this pod or not
+ if self.ownerHome().normal():
+ # Get the external home for the sharee
+ home = yield self.externalHome()
+
+ yield records[0].update(**{
+ self._bindHomeIDAttributeName: home.id(),
+ })
+ else:
+ # It is OK to just delete (as opposed to doing a full "unshare") without adjusting other things
+ # like sync revisions since those would not have been used for an external share anyway. Also,
+ # revisions are tied to the sharee calendar home id and that will be removed after migration
+ # is complete.
+ yield records[0].delete()
+
+ # Clean up external calendar if no sharees left
+ calendar = yield self.ownerView()
+ invites = yield calendar.sharingInvites()
+ if len(invites) == 0:
+ yield calendar.remove()
+ else:
+ raise AssertionError("We must have a bind record for this calendar.")
+
+
+ def externalHome(self):
+ """
+ Create and return an L{CommonHome} for the user being migrated. Note that when called, the user
+ directory record may still indicate that they are hosted on this pod, so we have to forcibly create
+ a home for the external user.
+ """
+ currentHome = self.viewerHome()
+ return self._txn.homeWithUID(currentHome._homeType, currentHome.uid(), status=_HOME_STATUS_EXTERNAL, create=True)
+
+
+ @inlineCallbacks
def _initBindRevision(self):
yield self.syncToken() # init self._syncTokenRevision if None
self._bindRevision = self._syncTokenRevision
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_tables.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_tables.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_tables.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -188,6 +188,7 @@
_HOME_STATUS_EXTERNAL = _homeStatus('external')
_HOME_STATUS_PURGING = _homeStatus('purging')
_HOME_STATUS_MIGRATING = _homeStatus('migrating')
+_HOME_STATUS_DISABLED = _homeStatus('disabled')
_bindStatus = _schemaConstants(
schema.CALENDAR_BIND_STATUS.DESCRIPTION,
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/test/util.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/test/util.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -303,10 +303,7 @@
yield cleanupTxn.commit()
# Deal with memcached items that must be cleared
- from txdav.caldav.datastore.sql import CalendarHome
- CalendarHome._cacher.flushAll()
- from txdav.carddav.datastore.sql import AddressBookHome
- AddressBookHome._cacher.flushAll()
+ storeToClean.queryCacher.flushAll()
from txdav.base.propertystore.sql import PropertyStore
PropertyStore._cacher.flushAll()
@@ -449,7 +446,7 @@
populateTxn._migrating = True
for homeUID in requirements:
calendars = requirements[homeUID]
- home = yield populateTxn.calendarHomeWithUID(homeUID, True)
+ home = yield populateTxn.calendarHomeWithUID(homeUID, create=True)
if calendars is not None:
# We don't want the default calendar or inbox to appear unless it's
# explicitly listed.
@@ -534,7 +531,7 @@
for homeUID in md5s:
calendars = md5s[homeUID]
if calendars is not None:
- home = yield populateTxn.calendarHomeWithUID(homeUID, True)
+ home = yield populateTxn.calendarHomeWithUID(homeUID, create=True)
for calendarName in calendars:
calendarObjNames = calendars[calendarName]
if calendarObjNames is not None:
@@ -565,7 +562,7 @@
for homeUID in requirements:
addressbooks = requirements[homeUID]
if addressbooks is not None:
- home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
+ home = yield populateTxn.addressbookHomeWithUID(homeUID, create=True)
# We don't want the default addressbook
try:
yield home.removeAddressBookWithName("addressbook")
@@ -602,7 +599,7 @@
for homeUID in md5s:
addressbooks = md5s[homeUID]
if addressbooks is not None:
- home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
+ home = yield populateTxn.addressbookHomeWithUID(homeUID, create=True)
for addressbookName in addressbooks:
addressbookObjNames = addressbooks[addressbookName]
if addressbookObjNames is not None:
@@ -883,32 +880,32 @@
@inlineCallbacks
- def homeUnderTest(self, txn=None, name="home1", create=False):
+ def homeUnderTest(self, txn=None, name="home1", status=None, create=False):
"""
Get the calendar home detailed by C{requirements['home1']}.
"""
if txn is None:
txn = self.transactionUnderTest()
- returnValue((yield txn.calendarHomeWithUID(name, create=create)))
+ returnValue((yield txn.calendarHomeWithUID(name, status=status, create=create)))
@inlineCallbacks
- def calendarUnderTest(self, txn=None, name="calendar_1", home="home1"):
+ def calendarUnderTest(self, txn=None, name="calendar_1", home="home1", status=None):
"""
Get the calendar detailed by C{requirements['home1']['calendar_1']}.
"""
returnValue((
- yield (yield self.homeUnderTest(txn, home)).calendarWithName(name)
+ yield (yield self.homeUnderTest(txn, name=home, status=status)).calendarWithName(name)
))
@inlineCallbacks
- def calendarObjectUnderTest(self, txn=None, name="1.ics", calendar_name="calendar_1", home="home1"):
+ def calendarObjectUnderTest(self, txn=None, name="1.ics", calendar_name="calendar_1", home="home1", status=None):
"""
Get the calendar detailed by
C{requirements[home][calendar_name][name]}.
"""
- returnValue((yield (yield self.calendarUnderTest(txn, name=calendar_name, home=home))
+ returnValue((yield (yield self.calendarUnderTest(txn, name=calendar_name, home=home, status=status))
.calendarObjectWithName(name)))
Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/work/test/test_revision_cleanup.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/work/test/test_revision_cleanup.py 2015-02-27 19:42:41 UTC (rev 14480)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/work/test/test_revision_cleanup.py 2015-02-27 19:59:39 UTC (rev 14481)
@@ -55,7 +55,7 @@
for homeUID in addressookRequirements:
addressbooks = addressookRequirements[homeUID]
if addressbooks is not None:
- home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
+ home = yield populateTxn.addressbookHomeWithUID(homeUID, create=True)
addressbook = home.addressbook()
addressbookObjNames = addressbooks[addressbook.name()]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150227/40aefaee/attachment-0001.html>
More information about the calendarserver-changes
mailing list