[CalendarServer-changes] [9721] CalendarServer/trunk/twistedcaldav/directory

source_changes at macosforge.org source_changes at macosforge.org
Thu Aug 16 18:54:54 PDT 2012


Revision: 9721
          http://trac.macosforge.org/projects/calendarserver/changeset/9721
Author:   sagen at apple.com
Date:     2012-08-16 18:54:53 -0700 (Thu, 16 Aug 2012)
Log Message:
-----------
Have GroupCacher update the cache token for principals whose group membership changes so the PROPFIND response cache is invalidated

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/aggregate.py
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/idirectory.py
    CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_livedirectory.py

Modified: CalendarServer/trunk/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/aggregate.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/aggregate.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -148,6 +148,18 @@
     def recordWithCalendarUserAddress(self, address):
         return self._queryAll("recordWithCalendarUserAddress", address)
 
+    def recordWithCachedGroupsAlias(self, recordType, alias):
+        """
+        @param recordType: the type of the record to look up.
+        @param alias: the cached-groups alias of the record to look up.
+        @type alias: C{str}
+
+        @return: a deferred L{IDirectoryRecord} with the given cached-groups
+            alias, or C{None} if no such record is found.
+        """
+        service = self.serviceForRecordType(recordType)
+        return service.recordWithCachedGroupsAlias(recordType, alias)
+
     @inlineCallbacks
     def recordsMatchingFields(self, fields, operand="or", recordType=None):
 

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -381,7 +381,7 @@
 
         guids = set()
 
-        self.log_info("Looking up which groups %s is a member of" % (guid,))
+        self.log_debug("Looking up which groups %s is a member of" % (guid,))
         try:
             self.log_debug("opendirectory.queryRecordsWithAttribute_list(%r,%r,%r,%r,%r,%r,%r)" % (
                 self.directory,
@@ -442,7 +442,7 @@
             if recordGUID:
                 guids.add(recordGUID)
 
-        self.log_info("%s is a member of %d groups" % (guid, len(guids)))
+        self.log_debug("%s is a member of %d groups" % (guid, len(guids)))
 
         return guids
 
@@ -1235,7 +1235,7 @@
 
         loop = 1
         while valuesToFetch:
-            self.log_info("getGroups loop %d" % (loop,))
+            self.log_debug("getGroups loop %d" % (loop,))
 
             results = []
 
@@ -1243,12 +1243,12 @@
                 fields = []
                 for value in batch:
                     fields.append(["guid", value, False, "equals"])
-                self.log_info("getGroups fetching batch of %d" %
+                self.log_debug("getGroups fetching batch of %d" %
                     (len(fields),))
                 result = list((yield self.recordsMatchingFields(fields,
                     recordType=self.recordType_groups)))
                 results.extend(result)
-                self.log_info("getGroups got back batch of %d for subtotal of %d" %
+                self.log_debug("getGroups got back batch of %d for subtotal of %d" %
                     (len(result), len(results)))
 
             # Reset values for next iteration
@@ -1262,7 +1262,7 @@
                 # record.nestedGUIDs() contains the sub groups of this group
                 for memberGUID in record.nestedGUIDs():
                     if memberGUID not in recordsByGUID:
-                        self.log_info("getGroups group %s contains group %s" %
+                        self.log_debug("getGroups group %s contains group %s" %
                             (record.guid, memberGUID))
                         valuesToFetch.add(memberGUID)
 

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -213,6 +213,18 @@
 
         return record if record and record.enabledForCalendaring else None
 
+    def recordWithCachedGroupsAlias(self, recordType, alias):
+        """
+        @param recordType: the type of the record to look up.
+        @param alias: the cached-groups alias of the record to look up.
+        @type alias: C{str}
+
+        @return: a deferred L{IDirectoryRecord} with the given cached-groups
+            alias, or C{None} if no such record is found.
+        """
+        # The default implementation uses guid
+        return succeed(self.recordWithGUID(alias))
+
     def allRecords(self):
         for recordType in self.recordTypes():
             for record in self.listRecords(recordType):
@@ -787,13 +799,6 @@
                 for member in groupMembers:
                     memberships = members.setdefault(member, set())
                     memberships.add(groupGUID)
-                    if member in previousMembers:
-                        # Remove from previousMembers; anything still left in
-                        # previousMembers when this loop is done will be
-                        # deleted from cache (since only members that were
-                        # previously in delegated-to groups but are no longer
-                        # would still be in previousMembers)
-                        del previousMembers[member]
 
             self.log_info("There are %d users delegated-to via groups" %
                 (len(members),))
@@ -813,15 +818,43 @@
 
         self.log_info("Storing %d group memberships in memcached" %
                        (len(members),))
+        changedMembers = set()
         for member, groups in members.iteritems():
             # self.log_debug("%s is in %s" % (member, groups))
             yield self.cache.setGroupsFor(member, groups)
+            if groups != previousMembers.get(member, None):
+                # This principal has had a change in group membership
+                # so invalidate the PROPFIND response cache
+                changedMembers.add(member)
+            try:
+                # Remove from previousMembers; anything still left in
+                # previousMembers when this loop is done will be
+                # deleted from cache (since only members that were
+                # previously in delegated-to groups but are no longer
+                # would still be in previousMembers)
+                del previousMembers[member]
+            except KeyError:
+                pass
 
         # Remove entries for principals that no longer are in delegated-to
         # groups
         for member, groups in previousMembers.iteritems():
             yield self.cache.deleteGroupsFor(member)
+            changedMembers.add(member)
 
+        # For principals whose group membership has changed, call groupsChanged()
+        if not fast and hasattr(self.directory, "principalCollection"):
+            for member in changedMembers:
+                record = yield self.directory.recordWithCachedGroupsAlias(
+                    self.directory.recordType_users, member)
+                if record is not None:
+                    principal = self.directory.principalCollection.principalForRecord(record)
+                    if principal is not None:
+                        self.log_debug("Group membership changed for %s (%s)" %
+                            (record.shortNames[0], record.guid,))
+                        if hasattr(principal, "groupsChanged"):
+                            yield principal.groupsChanged()
+
         yield self.cache.setPopulatedMarker()
 
         if useLock:
@@ -1033,6 +1066,14 @@
         from calendarserver.tap.util import directoryFromConfig
         directory = directoryFromConfig(config)
 
+        # We have to set cacheNotifierFactory otherwise group cacher can't
+        # invalidate the cache tokens for principals whose membership has
+        # changed
+        if config.EnableResponseCache and config.Memcached.Pools.Default.ClientEnabled:
+            from twistedcaldav.directory.principal import DirectoryPrincipalResource
+            from twistedcaldav.cache import MemcacheChangeNotifier
+            DirectoryPrincipalResource.cacheNotifierFactory = MemcacheChangeNotifier
+
         # Setup the ProxyDB Service
         proxydbClass = namedClass(config.ProxyDBService.type)
 

Modified: CalendarServer/trunk/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -81,6 +81,17 @@
             directory service may not be aware of these addresses.
         """
 
+    def recordWithCachedGroupsAlias(recordType, alias):
+        """
+        @param recordType: the type of the record to look up.
+        @param alias: the cached-groups alias of the record to look up.
+        @type alias: C{str}
+
+        @return: a deferred L{IDirectoryRecord} with the given cached-groups
+            alias, or C{None} if no such record is found.
+        """
+
+
     def recordsMatchingFields(fields):
         """
         @return: a deferred sequence of L{IDirectoryRecord}s which

Modified: CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -246,6 +246,9 @@
             # Also put the guidAttr attribute into the mappings for each type
             # so recordsMatchingFields can query on guid
             self.rdnSchema[recordType]["mapping"]["guid"] = self.rdnSchema["guidAttr"]
+            # Also put the memberIdAttr attribute into the mappings for each type
+            # so recordsMatchingFields can query on memberIdAttr
+            self.rdnSchema[recordType]["mapping"]["memberIdAttr"] = self.groupSchema["memberIdAttr"]
         if self.groupSchema["membersAttr"]:
             attrSet.add(self.groupSchema["membersAttr"])
         if self.groupSchema["nestedGroupsAttr"]:
@@ -346,6 +349,26 @@
 
         return records
 
+    @inlineCallbacks
+    def recordWithCachedGroupsAlias(self, recordType, alias):
+        """
+        @param recordType: the type of the record to look up.
+        @param alias: the cached-groups alias of the record to look up.
+        @type alias: C{str}
+
+        @return: a deferred L{IDirectoryRecord} with the given cached-groups
+            alias, or C{None} if no such record is found.
+        """
+        memberIdAttr = self.groupSchema["memberIdAttr"]
+        attributeToSearch = "memberIdAttr" if memberIdAttr else "dn"
+
+        fields = [[attributeToSearch, alias, False, "equals"]]
+        results = (yield self.recordsMatchingFields(fields, recordType=recordType))
+        if results:
+            returnValue(results[0])
+        else:
+            returnValue(None)
+
     def getExternalProxyAssignments(self):
         """
         Retrieve proxy assignments for locations and resources from the
@@ -1170,7 +1193,6 @@
         attributeToSearch = "guid"
         valuesToFetch = guids
 
-
         while valuesToFetch:
             results = []
 
@@ -1216,7 +1238,7 @@
             # Switch to the LDAP attribute used for identifying members
             # for subsequent iterations.  If memberIdAttr is not specified
             # in the config, we'll search using dn.
-            attributeToSearch = memberIdAttr if memberIdAttr else "dn"
+            attributeToSearch = "memberIdAttr" if memberIdAttr else "dn"
 
         returnValue(recordsByAlias.values())
 

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -957,6 +957,14 @@
     def expandedGroupMemberships(self):
         return self.groupMemberships(infinity=True)
 
+    def groupsChanged(self):
+        """
+        A callback indicating the directory group membership for this principal
+        has changed.  Update the cache token for this principal so the PROPFIND
+        response cache is invalidated.
+        """
+        return self.cacheNotifier.changed()
+
     def principalCollections(self):
         return self.parent.principalCollections()
 

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -43,6 +43,7 @@
                 "firstName" : "givenName",
                 "lastName" : "sn",
                 "guid" : "generateduid",
+                "memberIDAttr" : "generateduid",
             }
 
             entries = [

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_livedirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_livedirectory.py	2012-08-17 00:11:32 UTC (rev 9720)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_livedirectory.py	2012-08-17 01:54:53 UTC (rev 9721)
@@ -186,5 +186,8 @@
         class LiveODDirectoryServiceCase(LiveDirectoryTests, TestCase):
 
             def setUp(self):
-                params = {}
+                params = {
+                    "augmentService":
+                        augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,)),
+                }
                 self.svc = OpenDirectoryService(params)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120816/45e4ef60/attachment-0001.html>


More information about the calendarserver-changes mailing list