[CalendarServer-changes] [1926] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Sep 28 13:03:41 PDT 2007


Revision: 1926
          http://trac.macosforge.org/projects/calendarserver/changeset/1926
Author:   cdaboo at apple.com
Date:     2007-09-28 13:03:41 -0700 (Fri, 28 Sep 2007)

Log Message:
-----------
Merged branches/users/cdaboo/multiple-computer-records-1911 to trunk. Plus updated run for new PyOpenDirectory revision.

Modified Paths:
--------------
    CalendarServer/trunk/run
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py

Modified: CalendarServer/trunk/run
===================================================================
--- CalendarServer/trunk/run	2007-09-28 20:01:21 UTC (rev 1925)
+++ CalendarServer/trunk/run	2007-09-28 20:03:41 UTC (rev 1926)
@@ -480,7 +480,7 @@
   if ! py_have_module opendirectory; then
     opendirectory="${top}/PyOpenDirectory";
 
-    svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/trunk" 1631;
+    svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/trunk" 1925;
     py_build "PyOpenDirectory" "${opendirectory}" false;
     py_install "PyOpenDirectory" "${opendirectory}";
 

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2007-09-28 20:01:21 UTC (rev 1925)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2007-09-28 20:03:41 UTC (rev 1926)
@@ -33,12 +33,12 @@
 import dsattributes
 import dsquery
 
-from twisted.python import log
 from twisted.internet.reactor import callLater
 from twisted.internet.threads import deferToThread
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials
 
+from twistedcaldav import logging
 from twistedcaldav.config import config
 from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
 from twistedcaldav.directory.directory import DirectoryError, UnknownRecordTypeError
@@ -71,14 +71,15 @@
         try:
             directory = opendirectory.odInit(node)
         except opendirectory.ODError, e:
-            log.msg("Open Directory (node=%s) Initialization error: %s" % (node, e))
+            logging.err("Open Directory (node=%s) Initialization error: %s" % (node, e), system="OpenDirectoryService")
             raise
 
         self.realmName = node
         self.directory = directory
         self.node = node
         self.requireComputerRecord = requireComputerRecord
-        self.computerRecordName = ""
+        self.computerRecords = {}
+        self.servicetags = set()
         self._records = {}
         self._delayedCalls = set()
 
@@ -89,7 +90,7 @@
                 try:
                     self._lookupVHostRecord()
                 except Exception, e:
-                    log.err("Unable to locate virtual host record: %s" % (e,))
+                    logging.err("Unable to locate virtual host record: %s" % (e,), system="OpenDirectoryService")
                     raise
 
                 if os.path.exists(_serverPreferences):
@@ -100,7 +101,7 @@
                         'IsWorkgroupServer', False)
 
                     if self.isWorkgroupServer:
-                        log.msg("Enabling Workgroup Server compatibility mode")
+                        logging.info("Enabling Workgroup Server compatibility mode", system="OpenDirectoryService")
 
             for recordType in self.recordTypes():
                 self.recordsForType(recordType)
@@ -125,7 +126,7 @@
             if groupGUID in processedGUIDs:
                 continue
 
-            result = opendirectory.queryRecordsWithAttribute(
+            result = opendirectory.queryRecordsWithAttribute_list(
                 self.directory,
                 dsattributes.kDS1AttrGeneratedUID,
                 groupGUID,
@@ -136,10 +137,10 @@
                  dsattributes.kDSNAttrNestedGroups])
 
             if not result:
-                log.err("Couldn't find group %s when trying to expand nested groups." % (groupGUID,))
+                logging.err("Couldn't find group %s when trying to expand nested groups." % (groupGUID,), system="OpenDirectoryService")
                 continue
 
-            group = result.values()[0]
+            group = result[0][1]
 
             processedGUIDs.add(groupGUID)
 
@@ -178,7 +179,7 @@
                 % (self.realmName,)
             )
          
-        # Find a record in /Computers with an ENetAddress attribute value equal to the MAC address
+        # Find a record in /Computers with an apple-serviceinfo attribute value equal to the virtual hostname
         # and return some useful attributes.
         attrs = [
             dsattributes.kDS1AttrGeneratedUID,
@@ -187,7 +188,7 @@
             'dsAttrTypeNative:apple-serviceinfo',
         ]
 
-        records = opendirectory.queryRecordsWithAttributes(
+        records = opendirectory.queryRecordsWithAttributes_list(
             self.directory,
             dsquery.match(
                 'dsAttrTypeNative:apple-serviceinfo',
@@ -201,9 +202,6 @@
         self._parseComputersRecords(records, vhostname)
 
     def _parseComputersRecords(self, records, vhostname):
-        localNodePath = '/Local/Default'
-        localODNodePath = '/LDAPv3/127.0.0.1'
-
         # Must have some results
         if len(records) == 0:
             raise OpenDirectoryInitError(
@@ -211,73 +209,23 @@
                 % (self.realmName, vhostname,)
             )
 
-        # Now find a single record that actually matches the hostname
-        # Prefering the remote OD node to the local OD Node and
-        # the local OD Node to the local node.
+        # Now find all appropriate records and determine the enabled (only) service tags for each.
+        for recordname, record in records:
+            self._parseServiceInfo(vhostname, recordname, record)
 
-        _localNodes = []
-        _localODNodes = []
-        _remoteNodes = []
+        # Log all the matching records
+        for key, value in self.computerRecords.iteritems():
+            _ignore_recordname, enabled, servicetag = value
+            logging.info("Matched Directory record: %s with ServicesLocator: %s, state: %s" % (
+                key,
+                servicetag,
+                {True:"enabled", False:"disabled"}[enabled]
+            ), system="OpenDirectoryService")
 
-        for recordname, record in records.iteritems():
-            # May have an apple-serviceinfo
-            plist = record.get('dsAttrTypeNative:apple-serviceinfo', None)
-            if not plist:
-                continue
-
-            # XXX: Parse the plist so we can find only calendar vhosts with our hostname.
-            plistDict = readPlistFromString(plist)
-            vhosts = plistDict.get("com.apple.macosxserver.virtualhosts", None)
-            if not vhosts:
-                continue
-
-            hostguid = None
-            for key, value in vhosts.iteritems():
-                serviceTypes = value.get("serviceType", None)
-                if serviceTypes:
-                    for type in serviceTypes:
-                        if type == "calendar":
-                            hostguid = key
-                            break
-
-            if vhosts[hostguid].get("hostname", None) != vhostname:
-                continue
-
-            if record[dsattributes.kDSNAttrMetaNodeLocation] == localNodePath:
-                _localNodes.append((recordname, plist, record[dsattributes.kDS1AttrGeneratedUID]))
-
-            elif record[dsattributes.kDSNAttrMetaNodeLocation] == localODNodePath:
-                _localODNodes.append((recordname, plist, record[dsattributes.kDS1AttrGeneratedUID]))
-
-            else:
-                _remoteNodes.append((recordname, plist, record[dsattributes.kDS1AttrGeneratedUID]))
-
-        # Verify that we only have a single record type to match
-        if len(_remoteNodes) > 1:
-            raise OpenDirectoryInitError(
-                "Open Directory (node=%s) too many remote /Computers records (%s) were found matching virtual hostname: %s"
-                % (self.realmName, ", ".join([r[0] for r in _remoteNodes]), vhostname,)
-            )
-
-        if len(_remoteNodes) == 0 and len(_localODNodes) > 1:
-            raise OpenDirectoryInitError(
-                "Open Directory (node=%s) too many local OD /Computers records (%s) were found matching virtual hostname: %s"
-                % (self.realmName, ", ".join([r[0] for r in _localODNodes]), vhostname,)
-            )
-
-        if len(_remoteNodes) == 0 and len(_localODNodes) == 0 and len(_localNodes) > 1:
-            raise OpenDirectoryInitError(
-                "Open Directory (node=%s) too many local /Computers records (%s) were found matching virtual hostname: %s"
-                % (self.realmName, ", ".join([r[0] for r in _localNodes]), vhostname,)
-            )
-
-        # XXX: These calls to self._parseServiceInfo will cause the plist to be parsed _again_
-        #      refactor later so we only ever parse it once.
-
-        for node in itertools.chain(_remoteNodes, _localODNodes, _localNodes):
-            if node and self._parseServiceInfo(vhostname, *node):
-                break
-
+        # Log all the enabled service tags - or generate an error if there are none
+        if self.servicetags:
+            for tag in self.servicetags:
+                logging.info("Enabled ServicesLocator: %s" % (tag,), system="OpenDirectoryService")
         else:
             raise OpenDirectoryInitError(
                 "Open Directory (node=%s) no /Computers records with an enabled and valid "
@@ -285,15 +233,25 @@
                 % (self.realmName, vhostname,)
             )
 
-    def _parseServiceInfo(self, vhostname, recordname, plist, recordguid):
+    def _parseServiceInfo(self, vhostname, recordname, record):
+
+        # Extract some useful attributes
+        recordguid = record[dsattributes.kDS1AttrGeneratedUID]
+        recordlocation = "%s/Computers/%s" % (record[dsattributes.kDSNAttrMetaNodeLocation], recordname,)
+
+        # First check for apple-serviceinfo attribute
+        plist = record.get('dsAttrTypeNative:apple-serviceinfo', None)
+        if not plist:
+            return False
+
         # Parse the plist and look for our special entry
         plist = readPlistFromString(plist)
         vhosts = plist.get("com.apple.macosxserver.virtualhosts", None)
         if not vhosts:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have a "
-                "com.apple.macosxserver.virtualhosts in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
+            logging.err(
+                "Open Directory (node=%s) %s record does not have a "
+                "com.apple.macosxserver.virtualhosts in its apple-serviceinfo attribute value"
+                % (self.realmName, recordlocation,), system="OpenDirectoryService"
             )
             return False
         
@@ -308,70 +266,55 @@
                         break
                     
         if not hostguid:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have a "
-                "calendar service in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
-            )
+            # We can get false positives from the query - we ignore those.
             return False
             
         # Get host name
         hostname = vhosts[hostguid].get("hostname", None)
         if not hostname:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have "
-                "any host name in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
+            logging.err(
+                "Open Directory (node=%s) %s record does not have "
+                "any host name in its apple-serviceinfo attribute value"
+                % (self.realmName, recordlocation, ), system="OpenDirectoryService"
             )
             return False
         if hostname != vhostname:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record hostname (%s) "
-                "does not match this server (%s)"
-                % (self.realmName, recordname, hostname, vhostname)
-            )
+            # We can get false positives from the query - we ignore those.
             return False
         
-        # Get host details and create host templates
+        # Get host details. At this point we only check that it is present. We actually
+        # ignore the details themselves (scheme/port) as we use our own config for that.
         hostdetails = vhosts[hostguid].get("hostDetails", None)
         if not hostdetails:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have "
-                "any host details in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
+            logging.err(
+                "Open Directory (node=%s) %s record does not have "
+                "any host details in its apple-serviceinfo attribute value"
+                % (self.realmName, recordlocation, ), system="OpenDirectoryService"
             )
             return False
-        hostvariants = []
-        for key, value in hostdetails.iteritems():
-            if key in ("http", "https"):
-                hostvariants.append((key, hostname, value["port"]))
         
         # Look at the service data
         serviceInfos = vhosts[hostguid].get("serviceInfo", None)
         if not serviceInfos or not serviceInfos.has_key("calendar"):
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have a "
-                "calendar service in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
+            logging.err(
+                "Open Directory (node=%s) %s record does not have a "
+                "calendar service in its apple-serviceinfo attribute value"
+                % (self.realmName, recordlocation), system="OpenDirectoryService"
             )
             return False
         serviceInfo = serviceInfos["calendar"]
         
         # Check that this service is enabled
         enabled = serviceInfo.get("enabled", True)
-        if not enabled:
-            log.msg(
-                "Open Directory (node=%s) /Computers/%s record does not have an "
-                "enabled calendar service in its ServiceInfo attribute value"
-                % (self.realmName, recordname)
-            )
-            return False
 
         # Create the string we will use to match users with accounts on this server
-        self.servicetag = "%s:%s:calendar" % (recordguid, hostguid)
+        servicetag = "%s:%s:calendar" % (recordguid, hostguid)
         
-        self.computerRecordName = recordname
+        self.computerRecords[recordlocation] = (recordname, enabled, servicetag,)
         
+        if enabled:
+            self.servicetags.add(servicetag)
+        
         return True
     
     def _getCalendarUserAddresses(self, recordType, recordName, record):
@@ -431,8 +374,8 @@
 
                 def onError(f):
                     storage["status"] = "stale" # Keep trying
-                    log.err("Unable to load records of type %s from OpenDirectory due to unexpected error: %s"
-                            % (recordType, f))
+                    logging.err("Unable to load records of type %s from OpenDirectory due to unexpected error: %s"
+                            % (recordType, f), system="OpenDirectoryService")
 
                 d = deferToThread(self.reloadCache, recordType)
                 d.addErrback(onError)
@@ -470,8 +413,173 @@
             return None
 
     def reloadCache(self, recordType, shortName=None):
-        log.msg("Reloading %s record cache" % (recordType,))
+        
+        if shortName:
+            logging.info("Trying to add record %s to %s record cache" % (shortName, recordType,), system="OpenDirectoryService")
+        else:
+            logging.info("Reloading %s record cache" % (recordType,), system="OpenDirectoryService")
 
+        results = self._queryDirectory(recordType, shortName)
+        
+        records  = {}
+        guids    = {}
+        disabled_names = set()
+        disabled_guids = set()
+
+        for (key, value) in results:
+            enabledForCalendaring = True
+
+            if self.requireComputerRecord:
+                if not value.get(dsattributes.kDSNAttrServicesLocator):
+                    if recordType == DirectoryService.recordType_groups:
+                        enabledForCalendaring = False
+                        logging.debug("Group %s is not enabled for calendaring but may be used in ACLs" % (key,), system="OpenDirectoryService")
+                    else:
+                        logging.err("Directory (incorrectly) returned a record with no ServicesLocator attribute: %s" % (key,), system="OpenDirectoryService")
+                        continue
+
+            # Now get useful record info.
+            recordShortName = key
+            guid = value.get(dsattributes.kDS1AttrGeneratedUID)
+            if not guid:
+                continue
+            realName = value.get(dsattributes.kDS1AttrDistinguishedName)
+
+            # Get calendar user addresses from directory record.
+            if enabledForCalendaring:
+                calendarUserAddresses = self._getCalendarUserAddresses(recordType, key, value)
+            else:
+                calendarUserAddresses = ()
+
+            # Special case for groups.
+            if recordType == DirectoryService.recordType_groups:
+                memberGUIDs = value.get(dsattributes.kDSNAttrGroupMembers)
+                if memberGUIDs is None:
+                    memberGUIDs = ()
+                elif type(memberGUIDs) is str:
+                    memberGUIDs = (memberGUIDs,)
+                nestedGUIDs = value.get(dsattributes.kDSNAttrNestedGroups)
+                if nestedGUIDs:
+                    if type(nestedGUIDs) is str:
+                        nestedGUIDs = (nestedGUIDs,)
+                    memberGUIDs += tuple(nestedGUIDs)
+            else:
+                memberGUIDs = ()
+
+            # Special case for resources and locations
+            autoSchedule = False
+            proxyGUIDs = ()
+            if recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
+                resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
+                if resourceInfo is not None:
+                    autoSchedule, proxy = self._parseResourceInfo(resourceInfo)
+                    if proxy:
+                        proxyGUIDs = (proxy,)
+
+            record = OpenDirectoryRecord(
+                service               = self,
+                recordType            = recordType,
+                guid                  = guid,
+                shortName             = recordShortName,
+                fullName              = realName,
+                calendarUserAddresses = calendarUserAddresses,
+                autoSchedule          = autoSchedule,
+                enabledForCalendaring = enabledForCalendaring,
+                memberGUIDs           = memberGUIDs,
+                proxyGUIDs            = proxyGUIDs,
+            )
+            
+            # Check for disabled items
+            if recordShortName in disabled_names or guid in disabled_guids:
+                disabled_names.add(recordShortName)
+                disabled_guids.add(guid)
+                logging.warn("Record disabled: %s, with GUID: %s" % (recordShortName, guid,), system="OpenDirectoryService")
+            else:
+                # Check for duplicate items and disable all names/guids for mismatched duplicates.
+                ok_to_add = True
+                if recordShortName in records:
+                    existing_record = records[recordShortName]
+                    if existing_record.guid != guid:
+                        disabled_names.add(recordShortName)
+                        disabled_guids.add(guid)
+                        disabled_guids.add(existing_record.guid)
+                        del records[existing_record.shortName]
+                        del guids[existing_record.guid]
+                        logging.warn("Record disabled: %s, with GUID: %s" % (existing_record.shortName, existing_record.guid,), system="OpenDirectoryService")
+                        logging.warn("Record disabled: %s, with GUID: %s" % (recordShortName, guid,), system="OpenDirectoryService")
+                        ok_to_add = False
+                elif guid in guids:
+                    existing_record = guids[guid]
+                    if existing_record.shortName != recordShortName:
+                        disabled_names.add(recordShortName)
+                        disabled_guids.add(guid)
+                        disabled_names.add(existing_record.shortName)
+                        del records[existing_record.shortName]
+                        del guids[existing_record.guid]
+                        logging.warn("Record disabled: %s, with GUID: %s" % (existing_record.shortName, existing_record.guid,), system="OpenDirectoryService")
+                        logging.warn("Record disabled: %s, with GUID: %s" % (recordShortName, guid,), system="OpenDirectoryService")
+                        ok_to_add = False
+                
+                if ok_to_add:
+                    records[recordShortName] = guids[guid] = record
+                    logging.debug("Populated record: %s, with GUID: %s" % (records[recordShortName], guid,), system="OpenDirectoryService")
+
+        if shortName is None:
+            #
+            # Replace the entire cache
+            #
+            storage = {
+                "status"        : "new",
+                "records"       : records,
+                "guids"         : guids,
+                "disabled_names": disabled_names,
+                "disabled_guids": disabled_guids,
+            }
+
+            def rot():
+                storage["status"] = "stale"
+                removals = set()
+                for call in self._delayedCalls:
+                    if not call.active():
+                        removals.add(call)
+                for item in removals:
+                    self._delayedCalls.remove(item)
+
+            self._delayedCalls.add(callLater(recordListCacheTimeout, rot))
+
+            self._records[recordType] = storage
+
+        elif records:
+            #
+            # Update one record, if found
+            #
+            assert len(records) == 1, "shortName = %r, records = %r" % (shortName, len(records))
+            
+            # Need to check that this record has not been disabled
+            storage = self._records[recordType]
+            if shortName in storage["disabled_names"] or record.guid in storage["disabled_guids"]:
+                storage["disabled_guids"].add(record.guid)
+                storage["disabled_names"].add(shortName)
+                logging.warn("Record disabled: %s, with GUID: %s" % (shortName, record.guid,), system="OpenDirectoryService")
+                records = {}
+            elif record.guid in storage["guids"]:
+                # Got a duplicate GUID - for now we disable the record  being looked up. On the next cache refresh
+                # the existing record will also get disabled.
+                storage["disabled_guids"].add(record.guid)
+                storage["disabled_names"].add(shortName)
+                logging.warn("Record disabled: %s, with GUID: %s" % (shortName, record.guid,), system="OpenDirectoryService")
+                records = {}
+            else:
+                storage["records"][shortName] = record
+                storage["guids"][record.guid] = record
+
+        if shortName:
+            logging.info("Added %d records for %s in %s record cache" % (len(records), shortName, recordType,), system="OpenDirectoryService")
+        else:
+            logging.info("Found %d records for %s record cache" % (len(self._records[recordType]["guids"]), recordType,), system="OpenDirectoryService")
+
+    def _queryDirectory(self, recordType, shortName=None):
+
         attrs = [
             dsattributes.kDS1AttrGeneratedUID,
             dsattributes.kDS1AttrDistinguishedName,
@@ -503,7 +611,7 @@
         if self.requireComputerRecord:
             if self.isWorkgroupServer and recordType == DirectoryService.recordType_users:
                 if shortName is None:
-                    results = opendirectory.queryRecordsWithAttribute(
+                    results = opendirectory.queryRecordsWithAttribute_list(
                         self.directory,
                         dsattributes.kDSNAttrRecordName,
                         _saclGroup,
@@ -513,12 +621,16 @@
                         [dsattributes.kDSNAttrGroupMembers,
                          dsattributes.kDSNAttrNestedGroups])
 
-                    members = results.get(_saclGroup, {}).get(
-                        dsattributes.kDSNAttrGroupMembers, [])
+                    if len(results) == 1:
+                        members = results[0][1].get(
+                            dsattributes.kDSNAttrGroupMembers, [])
+    
+                        nestedGroups = results[0][1].get(
+                            dsattributes.kDSNAttrNestedGroups, [])
+                    else:
+                        members = []
+                        nestedGroups = []
 
-                    nestedGroups = results.get(_saclGroup, {}).get(
-                        dsattributes.kDSNAttrNestedGroups, [])
-
                     guidQueries = []
 
                     for GUID in self._expandGroupMembership(members,
@@ -536,7 +648,13 @@
             # have a services locator for this server.
             #
             elif recordType != DirectoryService.recordType_groups:
-                subquery = dsquery.match(dsattributes.kDSNAttrServicesLocator, self.servicetag, dsattributes.eDSExact)
+                tag_queries = []
+                for tag in self.servicetags:
+                    tag_queries.append(dsquery.match(dsattributes.kDSNAttrServicesLocator, tag, dsattributes.eDSExact))
+                if len(tag_queries) == 1:
+                    subquery = tag_queries[0]
+                else:
+                    subquery = dsquery.expression(dsquery.expression.OR, tag_queries)
                 if query is None:
                     query = subquery
                 else:
@@ -553,7 +671,7 @@
         try:
             if query:
                 if isinstance(query, dsquery.match):
-                    results = opendirectory.queryRecordsWithAttribute(
+                    results = opendirectory.queryRecordsWithAttribute_list(
                         self.directory,
                         query.attribute,
                         query.value,
@@ -563,7 +681,7 @@
                         attrs,
                     )
                 else:
-                    results = opendirectory.queryRecordsWithAttributes(
+                    results = opendirectory.queryRecordsWithAttributes_list(
                         self.directory,
                         query.generate(),
                         False,
@@ -571,117 +689,17 @@
                         attrs,
                     )
             else:
-                results = opendirectory.listAllRecordsWithAttributes(
+                results = opendirectory.listAllRecordsWithAttributes_list(
                     self.directory,
                     listRecordType,
                     attrs,
                 )
         except opendirectory.ODError, ex:
-            log.msg("Open Directory (node=%s) error: %s" % (self.realmName, str(ex)))
+            logging.err("Open Directory (node=%s) error: %s" % (self.realmName, str(ex)), system="OpenDirectoryService")
             raise
 
-        records = {}
-        guids   = {}
+        return results
 
-        for (key, value) in results.iteritems():
-            enabledForCalendaring = True
-
-            if self.requireComputerRecord:
-                if not value.get(dsattributes.kDSNAttrServicesLocator):
-                    if recordType == DirectoryService.recordType_groups:
-                        enabledForCalendaring = False
-                        #log.msg("Group %s is not enabled for calendaring but may be used in ACLs" % (key,))
-                    else:
-                        log.err("Directory (incorrectly) returned a record with no ServicesLocator attribute: %s" % (key,))
-                        continue
-
-            # Now get useful record info.
-            recordShortName = key
-            guid = value.get(dsattributes.kDS1AttrGeneratedUID)
-            if not guid:
-                continue
-            realName = value.get(dsattributes.kDS1AttrDistinguishedName)
-
-            # Get calendar user addresses from directory record.
-            if enabledForCalendaring:
-                calendarUserAddresses = self._getCalendarUserAddresses(recordType, key, value)
-            else:
-                calendarUserAddresses = ()
-
-            # Special case for groups.
-            if recordType == DirectoryService.recordType_groups:
-                memberGUIDs = value.get(dsattributes.kDSNAttrGroupMembers)
-                if memberGUIDs is None:
-                    memberGUIDs = ()
-                elif type(memberGUIDs) is str:
-                    memberGUIDs = (memberGUIDs,)
-                nestedGUIDs = value.get(dsattributes.kDSNAttrNestedGroups)
-                if nestedGUIDs:
-                    if type(nestedGUIDs) is str:
-                        nestedGUIDs = (nestedGUIDs,)
-                    memberGUIDs += tuple(nestedGUIDs)
-            else:
-                memberGUIDs = ()
-
-            # Special case for resources and locations
-            autoSchedule = False
-            proxyGUIDs = ()
-            if recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
-                resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
-                if resourceInfo is not None:
-                    autoSchedule, proxy = self._parseResourceInfo(resourceInfo)
-                    if proxy:
-                        proxyGUIDs = (proxy,)
-
-            record = OpenDirectoryRecord(
-                service               = self,
-                recordType            = recordType,
-                guid                  = guid,
-                shortName             = recordShortName,
-                fullName              = realName,
-                calendarUserAddresses = calendarUserAddresses,
-                autoSchedule          = autoSchedule,
-                enabledForCalendaring = enabledForCalendaring,
-                memberGUIDs           = memberGUIDs,
-                proxyGUIDs            = proxyGUIDs,
-            )
-            records[recordShortName] = guids[guid] = record
-
-            #log.debug("Populated record: %s" % (records[recordShortName],))
-
-        if shortName is None:
-            #
-            # Replace the entire cache
-            #
-            storage = {
-                "status" : "new",
-                "records": records,
-                "guids"  : guids,
-            }
-
-            def rot():
-                storage["status"] = "stale"
-                removals = set()
-                for call in self._delayedCalls:
-                    if not call.active():
-                        removals.add(call)
-                for item in removals:
-                    self._delayedCalls.remove(item)
-
-            self._delayedCalls.add(callLater(recordListCacheTimeout, rot))
-
-            self._records[recordType] = storage
-
-        elif records:
-            #
-            # Update one record, if found
-            #
-            assert len(records) == 1, "shortName = %r, records = %r" % (shortName, len(records))
-            storage = self._records[recordType]
-            storage["records"][shortName] = record
-            storage["guids"][record.guid] = record
-
-
 class OpenDirectoryRecord(DirectoryRecord):
     """
     Open Directory implementation of L{IDirectoryRecord}.
@@ -725,7 +743,7 @@
         for guid in self._proxyGUIDs:
             proxyRecord = self.service.recordWithGUID(guid)
             if proxyRecord is None:
-                log.err("No record for proxy in %s with GUID %s" % (self.shortName, guid))
+                logging.err("No record for proxy in %s with GUID %s" % (self.shortName, guid), system="OpenDirectoryService")
             else:
                 yield proxyRecord
 
@@ -742,8 +760,8 @@
             try:
                 return opendirectory.authenticateUserBasic(self.service.directory, self.guid, self.shortName, credentials.password)
             except opendirectory.ODError, e:
-                log.err("Open Directory (node=%s) error while performing basic authentication for user %s: %s"
-                        % (self.service.realmName, self.shortName, e))
+                logging.err("Open Directory (node=%s) error while performing basic authentication for user %s: %s"
+                        % (self.service.realmName, self.shortName, e), system="OpenDirectoryService")
                 return False
         elif isinstance(credentials, DigestedCredentials):
             try:
@@ -759,8 +777,8 @@
                                 'response="%(response)s",'
                                 'algorithm=%(algorithm)s') % credentials.fields
                 except KeyError, e:
-                    log.err("Open Directory (node=%s) error while performing digest authentication for user %s: missing digest response field: %s in: %s"
-                            % (self.service.realmName, self.shortName, e, credentials.fields))
+                    logging.err("Open Directory (node=%s) error while performing digest authentication for user %s: missing digest response field: %s in: %s"
+                            % (self.service.realmName, self.shortName, e, credentials.fields), system="OpenDirectoryService")
                     return False
 
                 return opendirectory.authenticateUserDigest(
@@ -772,8 +790,8 @@
                     credentials.method
                 )
             except opendirectory.ODError, e:
-                log.err("Open Directory (node=%s) error while performing digest authentication for user %s: %s"
-                        % (self.service.realmName, self.shortName, e))
+                logging.err("Open Directory (node=%s) error while performing digest authentication for user %s: %s"
+                        % (self.service.realmName, self.shortName, e), system="OpenDirectoryService")
                 return False
 
         return super(OpenDirectoryRecord, self).verifyCredentials(credentials)

Copied: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py (from rev 1924, CalendarServer/branches/users/cdaboo/multiple-computer-records-1911/twistedcaldav/directory/test/test_opendirectoryrecords.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py	2007-09-28 20:03:41 UTC (rev 1926)
@@ -0,0 +1,336 @@
+##
+# Copyright (c) 2005-2007 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.
+#
+# DRI: Wilfredo Sanchez, wsanchez at apple.com
+##
+
+import twisted.trial.unittest
+
+try:
+    from twistedcaldav.directory.appleopendirectory import OpenDirectoryService
+    import dsattributes
+except ImportError:
+    pass
+else:
+    from twistedcaldav.directory.directory import DirectoryService
+
+    def _queryDirectory(dir, recordType, shortName=None):
+
+        if shortName:
+            for name, record in dir.fakerecords[recordType]:
+                if name == shortName:
+                    return ((name, record),)
+            else:
+                return ()
+        else:
+            return dir.fakerecords[recordType]
+    
+    class ReloadCache(twisted.trial.unittest.TestCase):
+
+        def setUp(self):
+            super(ReloadCache, self).setUp()
+            self._service = OpenDirectoryService(node="/Search", dosetup=False)
+            OpenDirectoryService._queryDirectory = _queryDirectory
+            
+        def tearDown(self):
+            for call in self._service._delayedCalls:
+                call.cancel()
+
+        def _verifyRecords(self, recordType, expected):
+            expected = set(expected)
+            found = set(self._service._records[recordType]["records"].keys())
+            
+            missing = expected.difference(found)
+            extras = found.difference(expected)
+
+            self.assertTrue(len(missing) == 0, msg="Directory records not found: %s" % (missing,))
+            self.assertTrue(len(extras) == 0, msg="Directory records not expected: %s" % (extras,))
+                
+        def _verifyDisabledRecords(self, recordType, disabled_type, expected):
+            expected = set(expected)
+            found = self._service._records[recordType]["disabled_%s" % (disabled_type,)]
+            
+            missing = expected.difference(found)
+            extras = found.difference(expected)
+
+            self.assertTrue(len(missing) == 0, msg="Disabled directory records not found: %s" % (missing,))
+            self.assertTrue(len(extras) == 0, msg="Disabled directory records not expected: %s" % (extras,))
+
+        def test_Normal(self):
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+                DirectoryService.recordType_groups: [
+                    ["group01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_group01",
+                        dsattributes.kDS1AttrDistinguishedName: "Group 01",
+                        dsattributes.kDSNAttrEMailAddress: "group01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["group02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_group02",
+                        dsattributes.kDS1AttrDistinguishedName: "Group 02",
+                        dsattributes.kDSNAttrEMailAddress: "group02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+                DirectoryService.recordType_resources: [
+                    ["resource01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_resource01",
+                        dsattributes.kDS1AttrDistinguishedName: "Resource 01",
+                        dsattributes.kDSNAttrEMailAddress: "resource01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["resource02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_resource02",
+                        dsattributes.kDS1AttrDistinguishedName: "Resource 02",
+                        dsattributes.kDSNAttrEMailAddress: "resource02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+                DirectoryService.recordType_locations: [
+                    ["location01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_location01",
+                        dsattributes.kDS1AttrDistinguishedName: "Location 01",
+                        dsattributes.kDSNAttrEMailAddress: "location01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["location02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_location02",
+                        dsattributes.kDS1AttrDistinguishedName: "Location 02",
+                        dsattributes.kDSNAttrEMailAddress: "location02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+            self._service.reloadCache(DirectoryService.recordType_groups)
+            self._service.reloadCache(DirectoryService.recordType_resources)
+            self._service.reloadCache(DirectoryService.recordType_locations)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ())
+
+            self._verifyRecords(DirectoryService.recordType_groups, ("group01", "group02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_groups, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_groups, "guids", ())
+
+            self._verifyRecords(DirectoryService.recordType_resources, ("resource01", "resource02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_resources, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_resources, "guids", ())
+
+            self._verifyRecords(DirectoryService.recordType_locations, ("location01", "location02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_locations, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_locations, "guids", ())
+
+        def test_DuplicateRecords(self):
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ())
+
+
+        def test_DuplicateName(self):
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02-1",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02-2",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ("user02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ("GUID_user02-1", "GUID_user02-2", ))
+
+        def test_DuplicateGUID(self):
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user03", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ("user02", "user03",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ("GUID_user02", ))
+
+        def test_DuplicateCombo(self):
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user03", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02-2",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ("user02", "user03",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ("GUID_user02", "GUID_user02-2"))
+
+        def test_DuplicateGUIDCacheMiss(self):
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users)
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ())
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ())
+            
+            self._service.fakerecords = {
+                DirectoryService.recordType_users: [
+                    ["user01", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user01",
+                        dsattributes.kDS1AttrDistinguishedName: "User 01",
+                        dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user02", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                    ["user03", {
+                        dsattributes.kDS1AttrGeneratedUID: "GUID_user02",
+                        dsattributes.kDS1AttrDistinguishedName: "User 02",
+                        dsattributes.kDSNAttrEMailAddress: "user02 at example.com",
+                        dsattributes.kDSNAttrServicesLocator: "12345:67890:calendar",
+                    }],
+                ],
+            }
+
+            self._service.reloadCache(DirectoryService.recordType_users, "user03")
+
+            self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "names", ("user03",))
+            self._verifyDisabledRecords(DirectoryService.recordType_users, "guids", ("GUID_user02", ))
+

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py	2007-09-28 20:01:21 UTC (rev 1925)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py	2007-09-28 20:03:41 UTC (rev 1926)
@@ -948,7 +948,10 @@
         def test_plist_errors(self):
             def _doParse(plist, title):
                 service = OpenDirectoryService(node="/Search", dosetup=False)
-                if service._parseServiceInfo("calendar.apple.com", "recordit", plist, "GUIDIFY"):
+                if service._parseServiceInfo("calendar.apple.com", "recordit", {
+                'dsAttrTypeNative:apple-serviceinfo'  : plist,
+                dsattributes.kDS1AttrGeneratedUID:      "GUIDIFY",
+                dsattributes.kDSNAttrMetaNodeLocation:  "/LDAPv3/127.0.0.1"}) and service.servicetags:
                     self.fail(msg="Plist parse should have failed: %s" % (title,))
 
             plists = (
@@ -964,16 +967,22 @@
 
         def test_goodplist(self):
             service = OpenDirectoryService(node="/Search", dosetup=False)
-            if not service._parseServiceInfo("calendar.apple.com", "recordit", PlistParse.plist_good, "GUIDIFY"):
+            if not service._parseServiceInfo("calendar.apple.com", "recordit", {
+                'dsAttrTypeNative:apple-serviceinfo'  : PlistParse.plist_good,
+                dsattributes.kDS1AttrGeneratedUID:      "GUIDIFY",
+                dsattributes.kDSNAttrMetaNodeLocation:  "/LDAPv3/127.0.0.1"}):
                 self.fail(msg="Plist parse should not have failed")
             else:
                 # Verify that we extracted the proper items
-                self.assertEqual(service.servicetag, "GUIDIFY:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")
+                self.assertEqual(service.servicetags.pop(), "GUIDIFY:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")
 
         def test_expandcuaddrs(self):
             def _doTest(recordName, record, result, title):
                 service = OpenDirectoryService(node="/Search", dosetup=False)
-                if not service._parseServiceInfo("calendar.apple.com", recordName, PlistParse.plist_good, "GUIDIFY"):
+                if not service._parseServiceInfo("calendar.apple.com", recordName, {
+                'dsAttrTypeNative:apple-serviceinfo'  : PlistParse.plist_good,
+                dsattributes.kDS1AttrGeneratedUID:      "GUIDIFY",
+                dsattributes.kDSNAttrMetaNodeLocation:  "/LDAPv3/127.0.0.1"}):
                     self.fail(msg="Plist parse should not have failed: %s" % (recordName,))
                 else:
                     expanded = service._getCalendarUserAddresses(DirectoryService.recordType_users, recordName, record)
@@ -1073,10 +1082,10 @@
                     pass
 
             records = (
-                ({}, "no records found"),
-                ({
-                      ODRecordsParse.record_localod_good_other[0]  : ODRecordsParse.record_localod_good_other[1],
-                 }, "non-matching record found"),
+                ((), "no records found"),
+                ((
+                      (ODRecordsParse.record_localod_good_other[0], ODRecordsParse.record_localod_good_other[1]),
+                 ), "non-matching record found"),
             )
 
             for recordlist, title in records:
@@ -1091,71 +1100,79 @@
                     self.fail(msg="Record parse should not have failed: \"%s\" with error: %s" % (title, ex))
 
             records = (
-                ({
-                      ODRecordsParse.record_localod_good[0]        : ODRecordsParse.record_localod_good[1],
-                 }, "single good plist"),
-                ({
-                      ODRecordsParse.record_localod_good[0]        : ODRecordsParse.record_localod_good[1],
-                      ODRecordsParse.record_localod_good_other[0]  : ODRecordsParse.record_localod_good_other[1],
-                 }, "multiple plists"),
+                ((
+                      (ODRecordsParse.record_localod_good[0],       ODRecordsParse.record_localod_good[1]),
+                 ), "single good plist"),
+                ((
+                      (ODRecordsParse.record_localod_good[0],       ODRecordsParse.record_localod_good[1]),
+                      (ODRecordsParse.record_localod_good_other[0], ODRecordsParse.record_localod_good_other[1]),
+                 ), "multiple plists"),
             )
 
             for recordlist, title in records:
                 _doParseRecords(recordlist, title)
 
         def test_odrecords_multiple(self):
-            def _doParseRecords(recordlist, title, guid):
+            def _doParseRecords(recordlist, title, tags):
                 service = OpenDirectoryService(node="/Search", dosetup=False)
                 service._parseComputersRecords(recordlist, "calendar.apple.com")
-                gotGuid = service.servicetag.split(':', 1)[0]
 
-                self.assertEquals(guid, gotGuid,
-                                  "Got wrong guid, %s: Expected %s not %s" % (title, guid, gotGuid))
+                self.assertEquals(service.servicetags, set(tags),
+                                  "Got wrong service tags: %s and %s" % (service.servicetags, set(tags),))
 
             records = (
-                ({ODRecordsParse.record_remoteod_good[0] : ODRecordsParse.record_remoteod_good[1],
-                  ODRecordsParse.record_localod_good[0]  : ODRecordsParse.record_localod_good[1],
-                  ODRecordsParse.record_default_good[0]  : ODRecordsParse.record_default_good[1]},
-                 "Remote Record Preferred", "GUID2"),
-                ({ODRecordsParse.record_localod_good[0]  : ODRecordsParse.record_localod_good[1],
-                  ODRecordsParse.record_default_good[0]  : ODRecordsParse.record_default_good[1]},
-                 "Local OD Preferred", "GUID1"),
-                ({ODRecordsParse.record_default_good[0]  : ODRecordsParse.record_default_good[1]},
-                 "Local Node Preferred", "GUID3"),
+                (((ODRecordsParse.record_remoteod_good[0],  ODRecordsParse.record_remoteod_good[1]),
+                  (ODRecordsParse.record_localod_good[0],   ODRecordsParse.record_localod_good[1]),
+                  (ODRecordsParse.record_default_good[0],   ODRecordsParse.record_default_good[1])),
+                 "Three records",
+                 ("GUID2:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
+                (((ODRecordsParse.record_localod_good[0],   ODRecordsParse.record_localod_good[1]),
+                  (ODRecordsParse.record_default_good[0],   ODRecordsParse.record_default_good[1])),
+                 "Two records",
+                 ("GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
+                (((ODRecordsParse.record_default_good[0],   ODRecordsParse.record_default_good[1]),),
+                 "One record",
+                 ("GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",)),
             )
 
-            for recordlist, title, guid in records:
-                _doParseRecords(recordlist, title, guid)
+            for recordlist, title, tags in records:
+                _doParseRecords(recordlist, title, tags)
 
         def test_odrecords_duplicates(self):
-            def _doParseRecords(recordlist, title, items):
+            def _doParseRecords(recordlist, title, items, tags):
                 service = OpenDirectoryService(node="/Search", dosetup=False)
-                try:
-                    service._parseComputersRecords(recordlist, "calendar.apple.com")
-                except OpenDirectoryInitError, ex:
-                    for item in items:
-                        if item not in str(ex):
-                            self.fail(msg="Record parse should have failed: \"%s\" with error: %s containing %s" % (title, ex, item))
-                else:
-                    self.fail(msg="Record parse should have failed: \"%s\"" % (title, ))
+                service._parseComputersRecords(recordlist, "calendar.apple.com")
+                self.assertEquals(service.servicetags, set(tags))
 
             records = (
-                ({ODRecordsParse.record_remoteod_good[0]      : ODRecordsParse.record_remoteod_good[1],
-                  ODRecordsParse.record_remoteod_duplicate[0] : ODRecordsParse.record_remoteod_duplicate[1],
-                  ODRecordsParse.record_localod_good[0]       : ODRecordsParse.record_localod_good[1],
-                  ODRecordsParse.record_default_good[0]       : ODRecordsParse.record_default_good[1]},
-                 "Remote Record Duplicated", ("computer3.apple.com", "computer3",)),
-                ({ODRecordsParse.record_localod_good[0]       : ODRecordsParse.record_localod_good[1],
-                  ODRecordsParse.record_localod_duplicate[0]  : ODRecordsParse.record_localod_duplicate[1],
-                  ODRecordsParse.record_default_good[0]       : ODRecordsParse.record_default_good[1]},
-                 "Local OD Duplicated", ("computer1.apple.com", "computer1",)),
-                ({ODRecordsParse.record_default_good[0]       : ODRecordsParse.record_default_good[1],
-                  ODRecordsParse.record_default_duplicate[0]  : ODRecordsParse.record_default_duplicate[1]},
-                 "Local Node Duplicated", ("computer4.apple.com", "computer4",)),
+                (((ODRecordsParse.record_remoteod_good[0],       ODRecordsParse.record_remoteod_good[1]),
+                  (ODRecordsParse.record_remoteod_duplicate[0],  ODRecordsParse.record_remoteod_duplicate[1]),
+                  (ODRecordsParse.record_localod_good[0],        ODRecordsParse.record_localod_good[1]),
+                  (ODRecordsParse.record_default_good[0],        ODRecordsParse.record_default_good[1])),
+                 "Remote Record Duplicated", ("computer3.apple.com", "computer3",),
+                 ("GUID2:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID2:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar",
+                  "GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
+                (((ODRecordsParse.record_localod_good[0],        ODRecordsParse.record_localod_good[1]),
+                  (ODRecordsParse.record_localod_duplicate[0],   ODRecordsParse.record_localod_duplicate[1]),
+                  (ODRecordsParse.record_default_good[0],        ODRecordsParse.record_default_good[1])),
+                 "Local OD Duplicated", ("computer1.apple.com", "computer1",),
+                 ("GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID1_bad:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar",
+                  "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
+                (((ODRecordsParse.record_default_good[0],        ODRecordsParse.record_default_good[1]),
+                  (ODRecordsParse.record_default_duplicate[0],   ODRecordsParse.record_default_duplicate[1])),
+                 "Local Node Duplicated", ("computer4.apple.com", "computer4",),
+                 ("GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
+                  "GUID3:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar")),
             )
 
-            for recordlist, title, items in records:
-                _doParseRecords(recordlist, title, items)
+            for recordlist, title, items, tags in records:
+                _doParseRecords(recordlist, title, items, tags)
 
     class ODResourceInfoParse (twisted.trial.unittest.TestCase):
 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070928/215666c4/attachment.html


More information about the calendarserver-changes mailing list