[CalendarServer-changes] [7378] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Apr 27 18:17:23 PDT 2011


Revision: 7378
          http://trac.macosforge.org/projects/calendarserver/changeset/7378
Author:   cdaboo at apple.com
Date:     2011-04-27 18:17:23 -0700 (Wed, 27 Apr 2011)
Log Message:
-----------
Support multi-server, same domain, iSchedule federated servers. Merged from branch.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/provision/root.py
    CalendarServer/trunk/calendarserver/tap/util.py
    CalendarServer/trunk/calendarserver/tools/manageaugments.py
    CalendarServer/trunk/conf/auth/augments.dtd
    CalendarServer/trunk/conf/caldavd-partitioning-primary.plist
    CalendarServer/trunk/conf/caldavd-partitioning-secondary.plist
    CalendarServer/trunk/contrib/migration/calendarmigrator.py
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/augment.py
    CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/trunk/twistedcaldav/directory/common.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/idirectory.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/resource.py
    CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml
    CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml
    CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py
    CalendarServer/trunk/twistedcaldav/schedule.py
    CalendarServer/trunk/twistedcaldav/scheduling/addressmapping.py
    CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
    CalendarServer/trunk/twistedcaldav/scheduling/cuaddress.py
    CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule.py
    CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/test/test_config.py
    CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
    CalendarServer/trunk/twistedcaldav/upgrade.py

Added Paths:
-----------
    CalendarServer/trunk/conf/servers-test.xml
    CalendarServer/trunk/conf/servers.dtd
    CalendarServer/trunk/conf/servers.xml
    CalendarServer/trunk/twistedcaldav/servers.py
    CalendarServer/trunk/twistedcaldav/test/test_servers.py

Removed Paths:
-------------
    CalendarServer/trunk/calendarserver/tools/makepartition.py
    CalendarServer/trunk/conf/partitions-test.plist
    CalendarServer/trunk/conf/partitions.plist
    CalendarServer/trunk/lib-patches/PyDirector/
    CalendarServer/trunk/twistedcaldav/partitions.py
    CalendarServer/trunk/twistedcaldav/pdmonster.py

Property Changed:
----------------
    CalendarServer/trunk/
    CalendarServer/trunk/support/build.sh
    CalendarServer/trunk/txdav/caldav/datastore/index_file.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
    CalendarServer/trunk/txdav/carddav/datastore/index_file.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py


Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593

Modified: CalendarServer/trunk/calendarserver/provision/root.py
===================================================================
--- CalendarServer/trunk/calendarserver/provision/root.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/calendarserver/provision/root.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -345,13 +345,12 @@
                         ))
 
         # Look for forwarding
-        if config.Partitioning.Enabled:
-            remote_ip = request.headers.getRawHeaders('x-forwarded-for')
-            if remote_ip and len(remote_ip) == 1:
-                request.forwarded_for = remote_ip[0]
-                if not hasattr(request, "extendedLogItems"):
-                    request.extendedLogItems = {}
-                request.extendedLogItems["xff"] = remote_ip[0]
+        remote_ip = request.headers.getRawHeaders('x-forwarded-for')
+        if remote_ip and len(remote_ip) == 1:
+            request.forwarded_for = remote_ip[0]
+            if not hasattr(request, "extendedLogItems"):
+                request.extendedLogItems = {}
+            request.extendedLogItems["xff"] = remote_ip[0]
 
         if request.method == "PROPFIND" and not getattr(request, "notInCache", False) and len(segments) > 1:
             try:

Modified: CalendarServer/trunk/calendarserver/tap/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/util.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/calendarserver/tap/util.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -519,6 +519,7 @@
 
         ischedule = iScheduleResourceClass(
             root,
+            newStore,
         )
         root.putChild("ischedule", ischedule)
 

Deleted: CalendarServer/trunk/calendarserver/tools/makepartition.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/makepartition.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/calendarserver/tools/makepartition.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,178 +0,0 @@
-#!/usr/bin/env python
-##
-# Copyright (c) 2010 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 optparse import OptionParser
-from twext.python.plistlib import readPlist, writePlist
-import os
-import sys
-from twistedcaldav.config import ConfigurationError, Config
-from twistedcaldav.stdconfig import PListConfigProvider, DEFAULT_CONFIG
-from urlparse import urlparse
-
-def error(s):
-    print s
-    sys.exit(1)
-
-def createPrimary(options):
-    _createNode(options, True)
-    print "Created primary partition with node id '%s' and uri '%s'" % (options.nodeid, options.nodeurl,)
-
-def createSecondary(options):
-    _createNode(options, False)
-    print "Created secondary partition with node id '%s' and uri '%s'" % (options.nodeid, options.nodeurl,)
-
-def _createNode(options, isPrimary):
-
-    # Read in main plist
-    try:
-        dataDict = readPlist(options.conf)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Main plist file does not exist or is inaccessible: %s" % (options.conf,))
-    
-    # Look for includes
-    includes = dataDict.setdefault("Includes", [])
-    
-    # Make sure partitioning plist is included
-    primaryPlist = "caldavd-partitioning-primary.plist"
-    secondaryPlist = "caldavd-partitioning-secondary.plist"
-    partitioningPlistAdd = os.path.join(
-        os.path.dirname(options.conf),
-        primaryPlist if isPrimary else secondaryPlist,
-    )
-    partitioningPlistRemove = os.path.join(
-        os.path.dirname(options.conf),
-        secondaryPlist if isPrimary else primaryPlist,
-    )
-    if partitioningPlistAdd not in includes:
-        includes.append(partitioningPlistAdd)
-    if partitioningPlistRemove in includes:
-        includes.remove(partitioningPlistRemove)
-    
-    # Push out main plist change
-    try:
-        writePlist(dataDict, options.conf)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Could not write main plist file: %s" % (options.conf,))
-
-    # Now edit partitioning plist
-    try:
-        dataDict = readPlist(partitioningPlistAdd)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Partitioning plist file does not exist or is inaccessible: %s" % (partitioningPlistAdd,))
-    
-    # Need to adjust the node id, and host names
-    dataDict["Partitioning"]["ServerPartitionID"] = options.nodeid
-    
-    if not isPrimary:
-        _ignore_scheme, netloc, _ignore_path, _ignore_params, _ignore_query, _ignore_fragment = urlparse(options.primaryurl)
-        if ':' in netloc:
-            host = netloc.split(':')[0]
-        else:
-            host = netloc
-        dataDict["ProxyDBService"]["params"]["host"] = host
-        dataDict["Memcached"]["Pools"]["CommonToAllNodes"]["BindAddress"] = host
-    
-    # Push out partitioning plist change
-    try:
-        writePlist(dataDict, partitioningPlistAdd)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Could not write partitioning plist file: %s" % (partitioningPlistAdd,))
-
-def addOther(options):
-    _addOther(options.conf, options.nodeid, options.nodeurl)
-    print "Added partition with node id '%s' and uri '%s' to partitions plist" % (options.nodeid, options.nodeurl,)
-    
-def _addOther(conf, nodeid, nodeurl):
-    
-    # Read main plist
-    try:
-        cfg = Config(PListConfigProvider(DEFAULT_CONFIG))
-        cfg.load(conf)
-    except ConfigurationError:
-        raise RuntimeError("Could not parse as plist: '%s'" % (conf,))
-
-    # Read in the partitions plist
-    partitionsPlist = cfg.Partitioning.PartitionConfigFile
-    try:
-        dataDict = readPlist(partitionsPlist)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Partitions plist file does not exist or is inaccessible: %s" % (partitionsPlist,))
-
-    # See if node id already exists
-    if nodeid in [partition.get("uid", None) for partition in dataDict.get("partitions", ())]:
-        raise RuntimeError("Node '%s' already in partitions plist '%s'" % (nodeid, partitionsPlist,))
-    
-    # Add new information and write it out
-    dataDict.setdefault("partitions", []).append(
-        {
-            "uid": nodeid,
-            "url": nodeurl,
-        }
-    )
-    try:
-        writePlist(dataDict, partitionsPlist)
-    except (IOError, OSError):                                    
-        raise RuntimeError("Could not write partitions plist: %s" % (partitionsPlist,))
-
-def main():
-
-    usage = "%prog [options] MODE"
-    epilog = """
-MODE is one of primary|secondary|add
-
-  primary:   Create a new primary node (manages main DBs)
-  secondary: Create a new secondary node
-  add:       Add information for a new partition node on another machine
-"""
-    description = "Tool to setup CalendarServer partition node configuration files"
-    version = "%prog v1.0"
-    parser = OptionParser(usage=usage, description=description, version=version)
-    parser.epilog = epilog
-    parser.format_epilog = lambda _:epilog
-
-    parser.add_option("-c", "--conf", dest="conf",
-                      help="Directory where .plist files are stored", metavar="CONF")
-    parser.add_option("-n", "--nodeid", dest="nodeid",
-                      help="Node ID for this node", metavar="NODEID")
-    parser.add_option("-u", "--url", dest="nodeurl",
-                      help="URL of node being added", metavar="NODEURL")
-    parser.add_option("-p", "--primary", dest="primaryurl",
-                      help="URL of primary node", metavar="PRIMARYURL")
-
-    (options, args) = parser.parse_args()
-
-    if len(args) != 1:
-        parser.error("incorrect number of arguments")
-
-    # Make sure conf dir has the needed .plist files
-    if not os.path.exists(options.conf):
-        parser.error("Could not find '%s'" % (options.conf,))
-    confdir = os.path.dirname(options.conf)
-    if not os.path.exists(os.path.join(confdir, "caldavd-partitioning-primary.plist")):
-        parser.error("Could not find caldavd-partitioning-primary.plist in '%s'" % (confdir,))
-    if not os.path.exists(os.path.join(confdir, "caldavd-partitioning-secondary.plist")):
-        parser.error("Could not find caldavd-partitioning-secondary.plist in '%s'" % (confdir,))
-
-    # Handle each action
-    {
-        "primary"  : createPrimary,
-        "secondary": createSecondary,
-        "add"      : addOther,
-    }[args[0]](options)
-
-if __name__ == '__main__':
-    main()

Modified: CalendarServer/trunk/calendarserver/tools/manageaugments.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/manageaugments.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/calendarserver/tools/manageaugments.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 ##
-# Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2011 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.
@@ -57,13 +57,15 @@
                       help="OD GUID to manipulate", metavar="UID")
     parser.add_option("-i", "--uidfile", dest="uidfile",
                       help="File containing a list of GUIDs to manipulate", metavar="UIDFILE")
-    parser.add_option("-n", "--node", dest="node",
-                      help="Partition node to assign to UID", metavar="NODE")
+    parser.add_option("-s", "--server", dest="serverID",
+                      help="Server id to assign to UID", metavar="SERVER")
+    parser.add_option("-p", "--partition", dest="partitionID",
+                      help="Partition id to assign to UID", metavar="PARTITION")
     parser.add_option("-c", "--enable-calendar", action="store_true", dest="enable_calendar",
                       default=True, help="Enable calendaring for this UID: %default")
     parser.add_option("-a", "--enable-addressbooks", action="store_true", dest="enable_addressbook",
                       default=True, help="Enable calendaring for this UID: %default")
-    parser.add_option("-s", "--auto-schedule", action="store_true", dest="auto_schedule",
+    parser.add_option("-x", "--auto-schedule", action="store_true", dest="auto_schedule",
                       default=False, help="Enable auto-schedule for this UID: %default")
 
     (options, args) = parser.parse_args()
@@ -104,7 +106,8 @@
     return AugmentRecord(
         uid = uid,
         enabled = True,
-        hostedAt = options.node,
+        serverID = options.serverID,
+        partitionID = options.partitionID,
         enabledForCalendaring = options.enable_calendar,
         enabledForAddressBooks = options.enable_addressbook,
         autoSchedule = options.auto_schedule,
@@ -125,8 +128,6 @@
                     uids.append(line[:-1])
             
         if args[0] == "add":
-            if not options.node:
-                parser.error("Partition node must be specified when adding")
             yield augment.AugmentService.addAugmentRecords([makeRecord(uid, options) for uid in uids])
             for uid in uids:
                 print "Added uid '%s' to augment database" % (uid,)

Modified: CalendarServer/trunk/conf/auth/augments.dtd
===================================================================
--- CalendarServer/trunk/conf/auth/augments.dtd	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/conf/auth/augments.dtd	2011-04-28 01:17:23 UTC (rev 7378)
@@ -16,12 +16,13 @@
 
 <!ELEMENT augments (record*) >
 
-  <!ELEMENT record (uid, enable, hosted-at?, enable-calendar?, enable-addressbook?, auto-schedule?)>
+  <!ELEMENT record (uid, enable, (server-id, partition-id?)?, enable-calendar?, enable-addressbook?, auto-schedule?)>
     <!ATTLIST record repeat CDATA "1">
 
   <!ELEMENT uid                (#PCDATA)>
   <!ELEMENT enable             (#PCDATA)>
-  <!ELEMENT hosted-at          (#PCDATA)>
+  <!ELEMENT server-id          (#PCDATA)>
+  <!ELEMENT partition-id       (#PCDATA)>
   <!ELEMENT enable-calendar    (#PCDATA)>
   <!ELEMENT enable-addressbook (#PCDATA)>
   <!ELEMENT auto-schedule      (#PCDATA)>

Modified: CalendarServer/trunk/conf/caldavd-partitioning-primary.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-partitioning-primary.plist	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/conf/caldavd-partitioning-primary.plist	2011-04-28 01:17:23 UTC (rev 7378)
@@ -20,18 +20,18 @@
 <plist version="1.0">
   <dict>
 
-    <!--  Partitioning -->
-    <key>Partitioning</key>
+    <!--  Servers -->
+    <key>Servers</key>
     <dict>
 	    <key>Enabled</key>
 	    <true/>
-	    <key>ServerPartitionID</key>
-	    <string>00001</string>
-	    <key>PartitionConfigFile</key>
-	    <string>partitions.plist</string>
+	    <key>ConfigFile</key>
+	    <string>servers.xml</string>
 	    <key>MaxClients</key>
 	    <integer>5</integer>
 	</dict>
+    <key>ServerPartitionID</key>
+    <string>00001</string>
 
     <!-- PostgreSQL ProxyDB Service -->
     <key>ProxyDBService</key>

Modified: CalendarServer/trunk/conf/caldavd-partitioning-secondary.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-partitioning-secondary.plist	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/conf/caldavd-partitioning-secondary.plist	2011-04-28 01:17:23 UTC (rev 7378)
@@ -20,18 +20,18 @@
 <plist version="1.0">
   <dict>
 
-    <!--  Partitioning -->
-    <key>Partitioning</key>
+    <!--  Servers -->
+    <key>Servers</key>
     <dict>
 	    <key>Enabled</key>
 	    <true/>
-	    <key>ServerPartitionID</key>
-	    <string>00002</string>
-	    <key>PartitionConfigFile</key>
-	    <string>partitions.plist</string>
+	    <key>ConfigFile</key>
+	    <string>servers.xml</string>
 	    <key>MaxClients</key>
 	    <integer>5</integer>
 	</dict>
+    <key>ServerPartitionID</key>
+    <string>00002</string>
 
     <!-- PostgreSQL ProxyDB Service -->
     <key>ProxyDBService</key>

Deleted: CalendarServer/trunk/conf/partitions-test.plist
===================================================================
--- CalendarServer/trunk/conf/partitions-test.plist	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/conf/partitions-test.plist	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2009-2010 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-  <key>partitions</key>
-  <array>
-    <dict>
-      <key>uid</key>
-      <string>00001</string>
-      <key>url</key>
-      <string>http://localhost:8008</string>
-    </dict>
-    <dict>
-      <key>uid</key>
-      <string>00002</string>
-      <key>url</key>
-      <string>http://localhost:8108</string>
-    </dict>
-  </array>
-</dict>
-</plist>

Deleted: CalendarServer/trunk/conf/partitions.plist
===================================================================
--- CalendarServer/trunk/conf/partitions.plist	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/conf/partitions.plist	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-    Copyright (c) 2009-2010 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.
-  -->
-
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-  <key>partitions</key>
-  <array>
-  <!--
-    <dict>
-      <key>uid</key>
-      <string>00001</string>
-      <key>url</key>
-      <string>http://localhost:8008</string>
-    </dict>
-  -->
-  </array>
-</dict>
-</plist>

Copied: CalendarServer/trunk/conf/servers-test.xml (from rev 7377, CalendarServer/branches/users/cdaboo/pods/conf/servers-test.xml)
===================================================================
--- CalendarServer/trunk/conf/servers-test.xml	                        (rev 0)
+++ CalendarServer/trunk/conf/servers-test.xml	2011-04-28 01:17:23 UTC (rev 7378)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2011 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.
+ -->
+
+<!DOCTYPE servers SYSTEM "servers.dtd">
+
+<servers>
+  <server>
+    <id>00001</id>
+    <uri>http://localhost:8008</uri>
+    <partitions>
+    	<partition>
+    		<id>00001</id>
+    		<uri>http://localhost:8008</uri>
+    	</partition>
+    	<partition>
+    		<id>00002</id>
+    		<uri>http://localhost:8108</uri>
+    	</partition>
+    </partitions>
+  </server>
+</servers>

Copied: CalendarServer/trunk/conf/servers.dtd (from rev 7377, CalendarServer/branches/users/cdaboo/pods/conf/servers.dtd)
===================================================================
--- CalendarServer/trunk/conf/servers.dtd	                        (rev 0)
+++ CalendarServer/trunk/conf/servers.dtd	2011-04-28 01:17:23 UTC (rev 7378)
@@ -0,0 +1,26 @@
+<!--
+Copyright (c) 2011 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.
+-->
+
+<!ELEMENT servers (server*) >
+
+	<!ELEMENT server (id, uri, partitions?) >
+		<!ATTLIST server implicit (yes|no) "yes">
+
+		<!ELEMENT id  (#PCDATA) >
+		<!ELEMENT uri (#PCDATA) >
+
+		<!ELEMENT partitions (partition*) >
+			<!ELEMENT partition (id, uri) >

Copied: CalendarServer/trunk/conf/servers.xml (from rev 7377, CalendarServer/branches/users/cdaboo/pods/conf/servers.xml)
===================================================================
--- CalendarServer/trunk/conf/servers.xml	                        (rev 0)
+++ CalendarServer/trunk/conf/servers.xml	2011-04-28 01:17:23 UTC (rev 7378)
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2011 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.
+ -->
+
+<!DOCTYPE servers SYSTEM "servers.dtd">
+
+<servers>
+  <!--
+  <server>
+    <id>A</id>
+    <uri>https://caldav1.example.com:8843</uri>
+  </server>
+  <server>
+    <id>B</id>
+    <uri>https://caldav2.example.com:8843</uri>
+    <partitions>
+    	<partition>
+    		<id>00001</id>
+    		<url>https://machine1.example.com:8443</url>
+    	</partition>
+    	<partition>
+    		<id>00002</id>
+    		<url>https://machine2.example.com:8443</url>
+    	</partition>
+    </partitions>
+  </server>
+  -->
+</servers>

Modified: CalendarServer/trunk/contrib/migration/calendarmigrator.py
===================================================================
--- CalendarServer/trunk/contrib/migration/calendarmigrator.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/contrib/migration/calendarmigrator.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -107,7 +107,6 @@
 Memcached
 MultiProcess
 Notifications
-Partitioning
 Postgres
 ProcessType
 Profiling
@@ -128,6 +127,8 @@
 Scheduling
 ServerHostName
 ServerRoot
+Servers
+ServerPartitionID
 Sharing
 SudoersFile
 Twisted


Property changes on: CalendarServer/trunk/support/build.sh
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593
   + /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -325,6 +325,8 @@
             'appliesTo' : set([
                 dsattributes.kDSStdRecordTypeUsers,
                 dsattributes.kDSStdRecordTypeGroups,
+                dsattributes.kDSStdRecordTypeResources,
+                dsattributes.kDSStdRecordTypePlaces,
             ]),
         },
         'firstName' : {
@@ -351,6 +353,8 @@
             'appliesTo' : set([
                 dsattributes.kDSStdRecordTypeUsers,
                 dsattributes.kDSStdRecordTypeGroups,
+                dsattributes.kDSStdRecordTypeResources,
+                dsattributes.kDSStdRecordTypePlaces,
             ]),
         },
         'guid' : {
@@ -358,6 +362,8 @@
             'appliesTo' : set([
                 dsattributes.kDSStdRecordTypeUsers,
                 dsattributes.kDSStdRecordTypeGroups,
+                dsattributes.kDSStdRecordTypeResources,
+                dsattributes.kDSStdRecordTypePlaces,
             ]),
         },
     }
@@ -367,6 +373,10 @@
             dsattributes.kDSStdRecordTypeUsers,
         DirectoryService.recordType_groups :
             dsattributes.kDSStdRecordTypeGroups,
+        DirectoryService.recordType_resources :
+            dsattributes.kDSStdRecordTypeResources,
+        DirectoryService.recordType_locations :
+            dsattributes.kDSStdRecordTypePlaces,
     }
 
     _fromODRecordTypes = dict([(b, a) for a, b in _toODRecordTypes.iteritems()])
@@ -605,8 +615,17 @@
             ]
 
             if recordType == DirectoryService.recordType_users:
-                listRecordTypes = [dsattributes.kDSStdRecordTypeUsers]
+                listRecordTypes = [self._toODRecordTypes[recordType]]
 
+            elif recordType in (
+                DirectoryService.recordType_resources,
+                DirectoryService.recordType_locations,
+            ):
+                if queryattr == dsattributes.kDSNAttrEMailAddress:
+                    continue
+
+                listRecordTypes = [self._toODRecordTypes[recordType]]
+
             elif recordType == DirectoryService.recordType_groups:
 
                 if queryattr == dsattributes.kDSNAttrEMailAddress:

Modified: CalendarServer/trunk/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/augment.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/augment.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2011 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.
@@ -44,14 +44,16 @@
         self,
         uid,
         enabled=False,
-        hostedAt="",
+        serverID="",
+        partitionID="",
         enabledForCalendaring=False,
         autoSchedule=False,
         enabledForAddressBooks=False,
     ):
         self.uid = uid
         self.enabled = enabled
-        self.hostedAt = hostedAt
+        self.serverID = serverID
+        self.partitionID = partitionID
         self.enabledForCalendaring = enabledForCalendaring
         self.enabledForAddressBooks = enabledForAddressBooks
         self.autoSchedule = autoSchedule
@@ -296,13 +298,7 @@
 
             _ignore_etree, augments_node = newElementTreeWithRoot(xmlaugmentsparser.ELEMENT_AUGMENTS)
             for record in self.db.itervalues():
-                record_node = addSubElement(augments_node, xmlaugmentsparser.ELEMENT_RECORD)
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_UID, record.uid)
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_HOSTEDAT, record.hostedAt)
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if record.autoSchedule else "false")
+                self._addRecordToXMLDB(record, augments_node)
 
 
             writeXML(xmlfile, augments_node)
@@ -328,13 +324,7 @@
 
         # Create new record
         for record in records:
-            record_node = addSubElement(augments_node, xmlaugmentsparser.ELEMENT_RECORD)
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_UID, record.uid)
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_HOSTEDAT, record.hostedAt)
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
-            addSubElement(record_node, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if record.autoSchedule else "false")
+            self._addRecordToXMLDB(record, augments_node)
         
         # Modify xmlfile
         writeXML(xmlfile, augments_node)
@@ -360,16 +350,9 @@
             if uid in recordMap:
                 # Modify record
                 record = recordMap[uid]
-                del record_node.getchildren()[:]
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_UID, record.uid)
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_HOSTEDAT, record.hostedAt)
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
-                addSubElement(record_node, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if record.autoSchedule else "false")
+                self._updateRecordInXMLDB(record, record_node)
                 changed = True
-        
-        
+
         # Modify xmlfile
         if changed:
             writeXML(xmlfile, augments_node)
@@ -416,6 +399,21 @@
         if changed:
             writeXML(xmlfile, augments_node)
         
+        
+    def _addRecordToXMLDB(self, record, parentNode):
+        record_node = addSubElement(parentNode, xmlaugmentsparser.ELEMENT_RECORD)
+        self._updateRecordInXMLDB(record, record_node)
+
+    def _updateRecordInXMLDB(self, record, recordNode):
+        del recordNode.getchildren()[:]
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_UID, record.uid)
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLE, "true" if record.enabled else "false")
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_SERVERID, record.serverID)
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_PARTITIONID, record.partitionID)
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, "true" if record.enabledForCalendaring else "false")
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, "true" if record.enabledForAddressBooks else "false")
+        addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOSCHEDULE, "true" if record.autoSchedule else "false")
+
     def refresh(self):
         """
         Refresh any cached data.
@@ -499,9 +497,6 @@
     def __init__(self, dbID, dbapiName, dbapiArgs, **kwargs):
         
         AugmentDB.__init__(self)
-        self.cachedPartitions = {}
-        self.cachedHostedAt = {}
-        
         AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
         
     @inlineCallbacks
@@ -528,16 +523,17 @@
         """
         
         # Query for the record information
-        results = (yield self.query("select UID, ENABLED, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE from AUGMENTS where UID = :1", (uid,)))
+        results = (yield self.query("select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE from AUGMENTS where UID = :1", (uid,)))
         if not results:
             returnValue(None)
         else:
-            uid, enabled, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule = results[0]
+            uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule = results[0]
             
             record = AugmentRecord(
                 uid = uid,
                 enabled = enabled == "T",
-                hostedAt = (yield self._getPartition(partitionid)),
+                serverID = serverid,
+                partitionID = partitionid,
                 enabledForCalendaring = enabledForCalendaring == "T",
                 enabledForAddressBooks = enabledForAddressBooks == "T",
                 autoSchedule = autoSchedule == "T",
@@ -571,35 +567,6 @@
 
         return self.execute("delete from AUGMENTS", ())
         
-    @inlineCallbacks
-    def _getPartitionID(self, hostedat, createIfMissing=True):
-        
-        # We will use a cache for these as we do not expect changes whilst running
-        try:
-            returnValue(self.cachedHostedAt[hostedat])
-        except KeyError:
-            pass
-
-        partitionid = (yield self.queryOne("select PARTITIONID from PARTITIONS where HOSTEDAT = :1", (hostedat,)))
-        if partitionid == None:
-            yield self.execute("insert into PARTITIONS (HOSTEDAT) values (:1)", (hostedat,))
-            partitionid = (yield self.queryOne("select PARTITIONID from PARTITIONS where HOSTEDAT = :1", (hostedat,)))
-        self.cachedHostedAt[hostedat] = partitionid
-        returnValue(partitionid)
-
-    @inlineCallbacks
-    def _getPartition(self, partitionid):
-        
-        # We will use a cache for these as we do not expect changes whilst running
-        try:
-            returnValue(self.cachedPartitions[partitionid])
-        except KeyError:
-            pass
-
-        partition = (yield self.queryOne("select HOSTEDAT from PARTITIONS where PARTITIONID = :1", (partitionid,)))
-        self.cachedPartitions[partitionid] = partition
-        returnValue(partition)
-
     def _db_version(self):
         """
         @return: the schema version assigned to this index.
@@ -626,6 +593,7 @@
             (
                 ("UID",          "text unique"),
                 ("ENABLED",      "text(1)"),
+                ("SERVERID",     "text"),
                 ("PARTITIONID",  "text"),
                 ("CALENDARING",  "text(1)"),
                 ("ADDRESSBOOKS", "text(1)"),
@@ -634,19 +602,9 @@
             ifnotexists=True,
         )
 
-        yield self._create_table(
-            "PARTITIONS",
-            (
-                ("PARTITIONID",   "serial"),
-                ("HOSTEDAT",      "text"),
-            ),
-            ifnotexists=True,
-        )
-
     @inlineCallbacks
     def _db_empty_data_tables(self):
         yield self._db_execute("delete from AUGMENTS")
-        yield self._db_execute("delete from PARTITIONS")
 
 class AugmentSqliteDB(ADBAPISqliteMixin, AugmentADAPI):
     """
@@ -660,15 +618,15 @@
 
     @inlineCallbacks
     def _addRecord(self, record):
-        partitionid = (yield self._getPartitionID(record.hostedAt))
         yield self.execute(
             """insert or replace into AUGMENTS
-            (UID, ENABLED, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE)
-            values (:1, :2, :3, :4, :5, :6)""",
+            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE)
+            values (:1, :2, :3, :4, :5, :6, :7)""",
             (
                 record.uid,
                 "T" if record.enabled else "F",
-                partitionid,
+                record.serverID,
+                record.partitionID,
                 "T" if record.enabledForCalendaring else "F",
                 "T" if record.enabledForAddressBooks else "F",
                 "T" if record.autoSchedule else "F",
@@ -690,15 +648,15 @@
 
     @inlineCallbacks
     def _addRecord(self, record):
-        partitionid = (yield self._getPartitionID(record.hostedAt))
         yield self.execute(
             """insert into AUGMENTS
-            (UID, ENABLED, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE)
-            values (:1, :2, :3, :4, :5, :6)""",
+            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE)
+            values (:1, :2, :3, :4, :5, :6, :7)""",
             (
                 record.uid,
                 "T" if record.enabled else "F",
-                partitionid,
+                record.serverID,
+                record.partitionID,
                 "T" if record.enabledForCalendaring else "F",
                 "T" if record.enabledForAddressBooks else "F",
                 "T" if record.autoSchedule else "F",
@@ -707,15 +665,15 @@
 
     @inlineCallbacks
     def _modifyRecord(self, record):
-        partitionid = (yield self._getPartitionID(record.hostedAt))
         yield self.execute(
             """update AUGMENTS set
-            (UID, ENABLED, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE) =
-            (:1, :2, :3, :4, :5, :6) where UID = :7""",
+            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE) =
+            (:1, :2, :3, :4, :5, :6, :7) where UID = :8""",
             (
                 record.uid,
                 "T" if record.enabled else "F",
-                partitionid,
+                record.serverID,
+                record.partitionID,
                 "T" if record.enabledForCalendaring else "F",
                 "T" if record.enabledForAddressBooks else "F",
                 "T" if record.autoSchedule else "F",

Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -797,12 +797,13 @@
     PostgreSQL based augment database implementation.
     """
 
-    def __init__(self, host, database, user=None, password=None):
+    def __init__(self, host, database, user=None, password=None, dbtype=None):
         
         ADBAPIPostgreSQLMixin.__init__(self, )
         ProxyDB.__init__(self, "Proxies", "pgdb", (), host=host, database=database, user=user, password=password,)
+        if dbtype:
+            ProxyDB.schema_type = dbtype
 
-
 ##
 # Utilities
 ##

Modified: CalendarServer/trunk/twistedcaldav/directory/common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/common.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/common.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -81,8 +81,10 @@
         
         if record.locallyHosted():
             child = yield self.homeResourceCreator(record, transaction)
+        elif record.thisServer():
+            child = DirectoryReverseProxyResource(self, record)
         else:
-            child = DirectoryReverseProxyResource(self, record)
+            child = None # Use a redirect?
 
         returnValue(child)
 

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -43,8 +43,8 @@
 from twistedcaldav.config import config
 from twistedcaldav.directory.idirectory import IDirectoryService, IDirectoryRecord
 from twistedcaldav.directory.util import uuidFromName
-from twistedcaldav.partitions import partitions
 from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
+from twistedcaldav import servers
 
 class DirectoryService(LoggingMixIn):
     implements(IDirectoryService, ICredentialsChecker)
@@ -339,7 +339,7 @@
     implements(IDirectoryRecord)
 
     def __repr__(self):
-        return "<%s[%s@%s(%s)] %s(%s) %r @ %s>" % (
+        return "<%s[%s@%s(%s)] %s(%s) %r @ %s/#%s>" % (
             self.__class__.__name__,
             self.recordType,
             self.service.guid,
@@ -347,7 +347,8 @@
             self.guid,
             ",".join(self.shortNames),
             self.fullName,
-            self.hostedAt,
+            self.serverURI(),
+            self.partitionID,
         )
 
     def __init__(
@@ -377,7 +378,8 @@
         self.guid                   = guid
         self.uid                    = uid
         self.enabled                = False
-        self.hostedAt               = ""
+        self.serverID               = ""
+        self.partitionID            = ""
         self.shortNames             = shortNames
         self.authIDs                = authIDs
         self.fullName               = fullName
@@ -430,7 +432,8 @@
         
         if augment:
             self.enabled = augment.enabled
-            self.hostedAt = augment.hostedAt
+            self.serverID = augment.serverID
+            self.partitionID = augment.partitionID
             self.enabledForCalendaring = augment.enabledForCalendaring
             self.enabledForAddressBooks = augment.enabledForAddressBooks
             self.autoSchedule = augment.autoSchedule
@@ -447,8 +450,10 @@
         else:
             # Groups are by default always enabled
             self.enabled = (self.recordType == self.service.recordType_groups)
-            self.hostedAt = ""
+            self.serverID = ""
+            self.partitionID = ""
             self.enabledForCalendaring = False
+            self.enabledForAddressBooks = False
 
 
     def applySACLs(self):
@@ -495,12 +500,52 @@
                 return key
         return None
 
+    def serverURI(self):
+        """
+        URL of the server hosting this record. Return None if hosted on this server.
+        """
+        if config.Servers.Enabled and self.serverID:
+            return servers.Servers.getServerURIById(self.serverID)
+        else:
+            return None
+    
+    def server(self):
+        """
+        Server hosting this record. Return None if hosted on this server.
+        """
+        if config.Servers.Enabled and self.serverID:
+            return servers.Servers.getServerById(self.serverID)
+        else:
+            return None
+    
+    def partitionURI(self):
+        """
+        URL of the server hosting this record. Return None if hosted on this server.
+        """
+        if config.Servers.Enabled and self.serverID:
+            s = servers.Servers.getServerById(self.serverID)
+            if s:
+                return s.getPartitionURIForId(self.partitionID)
+        return None
+    
     def locallyHosted(self):
-        return not self.hostedAt or not config.Partitioning.Enabled or self.hostedAt == config.Partitioning.ServerPartitionID
-    
-    def hostedURL(self):
-        return partitions.getPartitionURL(self.hostedAt)
+        """
+        Hosted on this server/partition instance.
+        """
+        
+        if config.Servers.Enabled and self.serverID:
+            s = servers.Servers.getServerById(self.serverID)
+            if s:
+                return s.thisServer and (not self.partitionID or self.partitionID == config.ServerPartitionID)
+        return True
 
+    def thisServer(self):
+        if config.Servers.Enabled and self.serverID:
+            s = servers.Servers.getServerById(self.serverID)
+            if s:
+                return s.thisServer
+        return True
+        
 class DirectoryError(RuntimeError):
     """
     Generic directory error.

Modified: CalendarServer/trunk/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -103,7 +103,8 @@
     guid                  = Attribute("The GUID of this record.")
     uid                   = Attribute("The UID of this record.")
     enabled               = Attribute("Determines whether this record should be provisioned as a principal.")
-    hostedAt              = Attribute("Identifies the server that actually hosts data for the record.")
+    serverID              = Attribute("Identifies the server that actually hosts data for the record.")
+    partitionID           = Attribute("Identifies the partition node that actually hosts data for the record.")
     shortNames            = Attribute("The names for this record.")
     authIDs               = Attribute("Alternative security identities for this record.")
     fullName              = Attribute("The full name of this record.")

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -608,7 +608,8 @@
             """---------------------\n"""
             """Directory GUID: %s\n"""         % (self.record.service.guid,),
             """Realm: %s\n"""                  % (self.record.service.realmName,),
-            """Hosted-At: %s\n"""              % (self.record.hostedAt,) if config.Partitioning.Enabled else "", 
+            """Hosted-At: %s\n"""              % (self.record.serverURI(),) if config.Servers.Enabled else "", 
+            """Partition: %s\n"""              % (self.record.partitionID,) if config.Servers.Enabled and self.record.partitionID else "", 
             """\n"""
             """Principal Information\n"""
             """---------------------\n"""
@@ -767,12 +768,21 @@
     def principalUID(self):
         return self.record.uid
 
+    def serverURI(self):
+        return self.record.serverURI()
+
+    def server(self):
+        return self.record.server()
+
+    def partitionURI(self):
+        return self.record.partitionURI()
+
     def locallyHosted(self):
         return self.record.locallyHosted()
     
-    def hostedURL(self):
-        return self.record.hostedURL()
-
+    def thisServer(self):
+        return self.record.thisServer()
+    
     ##
     # Extra resource info
     ##
@@ -944,6 +954,11 @@
                 uidsResourceName,
                 self.record.uid
             ) + "/"
+            
+            # Prefix with other server if needed
+            if not self.thisServer():
+                self.calendarHomeURL = joinURL(self.serverURI(), self.calendarHomeURL)
+
         url = self.calendarHomeURL
         if url is None:
             return None
@@ -969,6 +984,11 @@
                 uidsResourceName,
                 self.record.uid
             ) + "/"
+            
+            # Prefix with other server if needed
+            if not self.thisServer():
+                self.addressBookHomeURL = joinURL(self.serverURI(), self.addressBookHomeURL)
+
         url = self.addressBookHomeURL
         if url is None:
             return None

Modified: CalendarServer/trunk/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/resource.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/resource.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -30,7 +30,7 @@
         self.parent = parent
         self.record = record
         
-        super(DirectoryReverseProxyResource, self).__init__(self.record.hostedAt)
+        super(DirectoryReverseProxyResource, self).__init__(self.record.partitionID)
     
     def url(self):
         return joinURL(self.parent.url(), self.record.uid)

Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments-test-default.xml	2011-04-28 01:17:23 UTC (rev 7378)
@@ -24,40 +24,40 @@
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
-    <hosted-at>00001</hosted-at>
+    <partition-id>00001</partition-id>
   </record>
   <record>
     <uid>Location-Default</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
-    <hosted-at>00004</hosted-at>
+    <partition-id>00004</partition-id>
     <auto-schedule>true</auto-schedule>
   </record>
   <record>
     <uid>Location-AA*</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
-    <hosted-at>00005</hosted-at>
+    <partition-id>00005</partition-id>
     <auto-schedule>true</auto-schedule>
   </record>
   <record>
     <uid>Resource-Default</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
-    <hosted-at>00006</hosted-at>
+    <partition-id>00006</partition-id>
     <auto-schedule>true</auto-schedule>
   </record>
   <record>
     <uid>Resource-AA*</uid>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
-    <hosted-at>00007</hosted-at>
+    <partition-id>00007</partition-id>
     <auto-schedule>true</auto-schedule>
   </record>
   <record>
     <uid>AA*</uid>
     <enable>true</enable>
-    <hosted-at>00001</hosted-at>
+    <partition-id>00001</partition-id>
   </record>
   <record>
     <uid>AB*</uid>
@@ -66,14 +66,14 @@
   <record>
     <uid>B*</uid>
     <enable>true</enable>
-    <hosted-at>00002</hosted-at>
+    <partition-id>00002</partition-id>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
   </record>
   <record>
     <uid>C*</uid>
     <enable>true</enable>
-    <hosted-at>00003</hosted-at>
+    <partition-id>00003</partition-id>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
     <auto-schedule>true</auto-schedule>
@@ -101,17 +101,17 @@
   <record>
     <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
-    <hosted-at>00001</hosted-at>
+    <partition-id>00001</partition-id>
   </record>
   <record>
     <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
     <enable>true</enable>
-    <hosted-at>00002</hosted-at>
+    <partition-id>00002</partition-id>
   </record>
   <record>
     <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
     <enable>true</enable>
-    <hosted-at>00002</hosted-at>
+    <partition-id>00002</partition-id>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>
     <auto-schedule>true</auto-schedule>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments-test.xml	2011-04-28 01:17:23 UTC (rev 7378)
@@ -42,17 +42,17 @@
   <record>
     <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
     <enable>true</enable>
-    <hosted-at>00001</hosted-at>
+    <partition-id>00001</partition-id>
   </record>
   <record>
     <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
     <enable>true</enable>
-    <hosted-at>00002</hosted-at>
+    <partition-id>00002</partition-id>
   </record>
   <record>
     <uid>6A73326A-F781-47E7-A9F8-AF47364D4152</uid>
     <enable>true</enable>
-    <hosted-at>00002</hosted-at>
+    <partition-id>00002</partition-id>
     <enable>true</enable>
     <enable-calendar>true</enable-calendar>
     <enable-addressbook>true</enable-addressbook>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_augment.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -28,41 +28,41 @@
 xmlFileDefault = os.path.join(os.path.dirname(__file__), "augments-test-default.xml")
 
 testRecords = (
-    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True,  "hostedAt":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False},
-    {"uid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "hostedAt":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True},
+    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True,  "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True,  "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False},
+    {"uid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True,  "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True,  "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True,  "partitionID":"00002", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True,  "partitionID":"00002", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":True},
 )
 
 testRecordWildcardDefault = (
-    {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
-    {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled":False, "hostedAt":"",      "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
-    {"uid":"C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled":True,  "hostedAt":"00003", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":True },
-    {"uid":"AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled":True,  "hostedAt":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled":False, "hostedAt":"",      "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
-    {"uid":"BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled":True,  "hostedAt":"00002", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
-    {"uid":"CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled":True,  "hostedAt":"00003", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":True },
+    {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "partitionID":"00001", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
+    {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA", "enabled":False, "partitionID":"",      "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"BC22A734-5E41-4FB7-B5C1-51DC0656DC2F", "enabled":True,  "partitionID":"00002", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
+    {"uid":"C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A", "enabled":True,  "partitionID":"00003", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":True },
+    {"uid":"AA859321-2C72-4974-ADCF-0CBA0C76F95D", "enabled":True,  "partitionID":"00001", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"AB7C488B-9ED2-4265-881C-7E2E38A63584", "enabled":False, "partitionID":"",      "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"BB0C0DA1-0545-45F6-8D08-917C554D93A4", "enabled":True,  "partitionID":"00002", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":False},
+    {"uid":"CCD30AD3-582F-4682-8B65-2EDE92C5656E", "enabled":True,  "partitionID":"00003", "enabledForCalendaring":True,  "enabledForAddressBooks":True,  "autoSchedule":True },
 )
 
 testRecordTypeDefault = (
-    ("locations", {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "hostedAt":"00004", "enabledForCalendaring":True,  "enabledForAddressBooks":False,  "autoSchedule":True}),
-    ("locations", {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "hostedAt":"00005", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True}),
-    ("resources", {"uid":"A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "hostedAt":"00006", "enabledForCalendaring":True,  "enabledForAddressBooks":False,  "autoSchedule":True}),
-    ("resources", {"uid":"AA6F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "hostedAt":"00007", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True}),
+    ("locations", {"uid":"A4318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "partitionID":"00004", "enabledForCalendaring":True,  "enabledForAddressBooks":False,  "autoSchedule":True}),
+    ("locations", {"uid":"AA5F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "partitionID":"00005", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True}),
+    ("resources", {"uid":"A5318887-F2C7-4A70-9056-B88CC8DB26F1", "enabled":True,  "partitionID":"00006", "enabledForCalendaring":True,  "enabledForAddressBooks":False,  "autoSchedule":True}),
+    ("resources", {"uid":"AA6F935F-3358-4510-A649-B391D63279F2", "enabled":True,  "partitionID":"00007", "enabledForCalendaring":True, "enabledForAddressBooks":False, "autoSchedule":True}),
 )
 
 
 testAddRecords = (
-    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True,  "hostedAt":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
+    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True,  "partitionID":"", "enabledForCalendaring":False, "enabledForAddressBooks":False, "autoSchedule":False},
 )
 
 testModifyRecords = (
-    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True,  "hostedAt":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False},
+    {"uid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975767", "enabled":True,  "partitionID":"", "enabledForCalendaring":True, "enabledForAddressBooks":True, "autoSchedule":False},
 )
 
 

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -322,6 +322,8 @@
                             dsattributes.kDSNAttrRecordType : dsattributes.kDSStdRecordTypeGroups,
                         },
                     ),
+                    dsattributes.kDSStdRecordTypePlaces : (),
+                    dsattributes.kDSStdRecordTypeResources : (),
                 }
 
                 def attributeMatches(fieldValue, value, caseless, matchType):

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaugmentsparser.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2011 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.
@@ -35,7 +35,8 @@
 
 ELEMENT_UID               = "uid"
 ELEMENT_ENABLE            = "enable"
-ELEMENT_HOSTEDAT          = "hosted-at"
+ELEMENT_SERVERID          = "server-id"
+ELEMENT_PARTITIONID       = "partition-id"
 ELEMENT_ENABLECALENDAR    = "enable-calendar"
 ELEMENT_ENABLEADDRESSBOOK = "enable-addressbook"
 ELEMENT_AUTOSCHEDULE      = "auto-schedule"
@@ -48,7 +49,8 @@
 ELEMENT_AUGMENTRECORD_MAP = {
     ELEMENT_UID:               "uid",
     ELEMENT_ENABLE:            "enabled",
-    ELEMENT_HOSTEDAT:          "hostedAt",
+    ELEMENT_SERVERID:          "serverID",
+    ELEMENT_PARTITIONID:       "partitionID",
     ELEMENT_ENABLECALENDAR:    "enabledForCalendaring",
     ELEMENT_ENABLEADDRESSBOOK: "enabledForAddressBooks",
     ELEMENT_AUTOSCHEDULE:      "autoSchedule",
@@ -91,7 +93,8 @@
                 
                 if node.tag in (
                     ELEMENT_UID,
-                    ELEMENT_HOSTEDAT,
+                    ELEMENT_SERVERID,
+                    ELEMENT_PARTITIONID,
                 ):
                     fields[node.tag] = node.text if node.text else ""
                 elif node.tag in (

Deleted: CalendarServer/trunk/twistedcaldav/partitions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/partitions.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/partitions.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,72 +0,0 @@
-##
-# Copyright (c) 2009-2010 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 twext.python.log import Logger
-from twext.python.plistlib import readPlist
-
-from twistedcaldav.client.pool import installPool
-
-"""
-Collection of classes for managing partition information for a group of servers.
-"""
-
-log = Logger()
-
-class Partitions(object):
-
-    def __init__(self):
-        
-        self.clear()
-
-    def clear(self):
-        self.partitions = {}
-        self.ownUID = ""
-        self.maxClients = 5
-
-    def readConfig(self, plistpath):
-        try:
-            dataDict = readPlist(plistpath)
-        except (IOError, OSError):                                    
-            log.error("Configuration file does not exist or is inaccessible: %s" % (self._configFileName,))
-            return
-        
-        for partition in dataDict.get("partitions", ()):
-            uid = partition.get("uid", None)
-            url = partition.get("url", None)
-            if uid and url:
-                self.partitions[uid] = url
-
-    def setSelfPartition(self, uid):
-        self.ownUID = uid
-
-    def setMaxClients(self, maxClients):
-        self.maxClients = maxClients
-
-    def getPartitionURL(self, uid):
-        # When the UID matches this server return an empty string
-        return self.partitions.get(uid, None) if uid != self.ownUID else ""
-
-    def installReverseProxies(self):
-        
-        for partition, url in self.partitions.iteritems():
-            if partition != self.ownUID:
-                installPool(
-                    partition,
-                    url,
-                    self.maxClients,
-                )
-
-partitions = Partitions()

Deleted: CalendarServer/trunk/twistedcaldav/pdmonster.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/pdmonster.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/pdmonster.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -1,62 +0,0 @@
-from twisted.internet import protocol
-from twisted.internet import address
-from twisted.protocols import amp
-
-from twext.web2.resource import WrapperResource
-
-from twext.python.log import LoggingMixIn
-
-class PDClientAddressWrapper(WrapperResource, LoggingMixIn):
-    def __init__(self, resource, socket, directory):
-        super(PDClientAddressWrapper, self).__init__(resource)
-
-        self.socket = socket
-        self.client = None
-        self.protocol = None
-        
-        self.directory = directory
-        
-    def getDirectory(self):
-        return self.directory
-
-    def hook(self, request):
-        from twisted.internet import reactor
-
-        def _gotProtocol(proto):
-            self.protocol = proto
-
-            return self.hook(request)
-
-        def _gotError(result):
-            result.trap(amp.RemoteAmpError)
-            if result.value.errorCode != 'UNKNOWN_PORT':
-                return result
-            self.log_error('Unknown Port: %s' % (request.remoteAddr,))
-
-        def _gotAddress(result):
-            self.log_debug('result = %r' % (result,))
-            request.remoteAddr = address.IPv4Address(
-                'TCP',
-                result['host'],
-                int(result['port']))
-            request._pdRewritten = True
-
-        if self.protocol is not None:
-            if hasattr(request, '_pdRewritten'):
-                return
-
-            host, port = request.remoteAddr.host, request.remoteAddr.port
-            self.log_debug("GetClientAddress(host=%r, port=%r)" % (host, port))
-            d = self.protocol.callRemoteString("GetClientAddress",
-                                                  host=host,
-                                                  port=str(port))
-            d.addCallbacks(_gotAddress, _gotError)
-            return d
-
-        else:
-            self.client = protocol.ClientCreator(reactor, amp.AMP)
-
-            d = self.client.connectUNIX(self.socket)
-            d.addCallback(_gotProtocol)
-
-            return d

Modified: CalendarServer/trunk/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/schedule.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/schedule.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -379,7 +379,7 @@
     Extends L{DAVResource} to provide iSchedule inbox functionality.
     """
 
-    def __init__(self, parent):
+    def __init__(self, parent, store):
         """
         @param parent: the parent resource of this one.
         """
@@ -388,6 +388,7 @@
         DAVResource.__init__(self, principalCollections=parent.principalCollections())
 
         self.parent = parent
+        self._newStore = store
 
     def deadProperties(self):
         if not hasattr(self, "_dead_properties"):
@@ -412,6 +413,13 @@
     def isPseudoCalendarCollection(self):
         return False
 
+    def principalForCalendarUserAddress(self, address):
+        for principalCollection in self.principalCollections():
+            principal = principalCollection.principalForCalendarUserAddress(address)
+            if principal is not None:
+                return principal
+        return None
+
     def render(self, request):
         output = """<html>
 <head>
@@ -438,8 +446,18 @@
         # This is a server-to-server scheduling operation.
         scheduler = IScheduleScheduler(request, self)
 
+        # Need a transaction to work with
+        txn = self._newStore.newTransaction("new transaction for Server To Server Inbox Resource")
+        request._newStoreTransaction = txn
+         
         # Do the POST processing treating this as a non-local schedule
-        result = (yield scheduler.doSchedulingViaPOST(use_request_headers=True))
+        try:
+            result = (yield scheduler.doSchedulingViaPOST(use_request_headers=True))
+        except Exception, e:
+            yield txn.abort()
+            raise e
+        else:
+            yield txn.commit()
         returnValue(result.response())
 
     ##

Modified: CalendarServer/trunk/twistedcaldav/scheduling/addressmapping.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/addressmapping.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/addressmapping.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -24,9 +24,8 @@
 from twistedcaldav.scheduling.delivery import DeliveryService
 from twistedcaldav.scheduling.imip import ScheduleViaIMip
 from twistedcaldav.scheduling.ischedule import ScheduleViaISchedule
-from twistedcaldav.scheduling.cuaddress import LocalCalendarUser,\
-    RemoteCalendarUser, EmailCalendarUser, InvalidCalendarUser,\
-    PartitionedCalendarUser
+from twistedcaldav.scheduling.cuaddress import RemoteCalendarUser, EmailCalendarUser, InvalidCalendarUser,\
+    calendarUserFromPrincipal
 
 __all__ = [
     "ScheduleAddressMapper",
@@ -54,7 +53,7 @@
         
         # If we have a principal always treat the user as local or partitioned
         if principal:
-            returnValue(LocalCalendarUser(cuaddr, principal) if principal.locallyHosted() else PartitionedCalendarUser(cuaddr, principal))
+            returnValue(calendarUserFromPrincipal(cuaddr, principal))
 
         # Get the type
         cuaddr_type = (yield self.getCalendarUserServiceType(cuaddr))

Modified: CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/caldav.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/caldav.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -39,7 +39,7 @@
 from twistedcaldav.method import report_common
 from twistedcaldav.resource import isCalendarCollectionResource
 from twistedcaldav.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser,\
-    PartitionedCalendarUser
+    PartitionedCalendarUser, OtherServerCalendarUser
 from twistedcaldav.scheduling.delivery import DeliveryService
 from twistedcaldav.scheduling.itip import iTIPRequestStatus
 from twistedcaldav.scheduling.processing import ImplicitProcessor, ImplicitProcessorException
@@ -99,7 +99,7 @@
         uid = self.scheduler.calendar.resourceUID()
 
         organizerPrincipal = None
-        if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser,):
+        if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,):
             organizerPrincipal = davxml.Principal(davxml.HRef(self.scheduler.organizer.principal.principalURL()))
 
         for recipient in self.recipients:

Modified: CalendarServer/trunk/twistedcaldav/scheduling/cuaddress.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/cuaddress.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/cuaddress.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -21,6 +21,7 @@
 __all__ = [
     "LocalCalendarUser",
     "PartitionedCalendarUser",
+    "OtherServerCalendarUser",
     "RemoteCalendarUser",
     "EmailCalendarUser",
     "InvalidCalendarUser",
@@ -54,6 +55,15 @@
     def __str__(self):
         return "Partitioned calendar user: %s" % (self.cuaddr,)
 
+class OtherServerCalendarUser(CalendarUser):
+    def __init__(self, cuaddr, principal):
+        self.cuaddr = cuaddr
+        self.principal = principal
+        self.serviceType = DeliveryService.serviceType_ischedule
+
+    def __str__(self):
+        return "Other server calendar user: %s" % (self.cuaddr,)
+
 class RemoteCalendarUser(CalendarUser):
     def __init__(self, cuaddr):
         self.cuaddr = cuaddr
@@ -104,3 +114,15 @@
         return addr.rstrip("/")
     else:
         return addr
+
+def calendarUserFromPrincipal(recipient, principal, inbox=None, inboxURL=None):
+    """
+    Get the appropriate calendar user address class for the provided principal.
+    """
+    
+    if principal.locallyHosted():
+        return LocalCalendarUser(recipient, principal, inbox, inboxURL)
+    elif principal.thisServer():
+        return PartitionedCalendarUser(recipient, principal)
+    else:
+        return OtherServerCalendarUser(recipient, principal)

Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -27,7 +27,7 @@
 from twistedcaldav.ical import Property
 from twistedcaldav.scheduling import addressmapping
 from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser,\
-    LocalCalendarUser, PartitionedCalendarUser
+    LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser
 from twistedcaldav.scheduling.icaldiff import iCalDiff
 from twistedcaldav.scheduling.itip import iTipGenerator, iTIPRequestStatus
 from twistedcaldav.scheduling.scheduler import CalDAVScheduler
@@ -915,7 +915,7 @@
     def checkOrganizerScheduleAgent(self):
 
         is_server = self.calendar.getOrganizerScheduleAgent()
-        local_organizer = isinstance(self.organizerAddress, LocalCalendarUser)
+        local_organizer = type(self.organizerAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
 
         if config.Scheduling.iMIP.Enabled and self.organizerAddress.cuaddr.lower().startswith("mailto:"):
             return True
@@ -943,7 +943,7 @@
         calendar_resource, _ignore_name, _ignore_collection, _ignore_uri = (yield getCalendarObjectForPrincipals(self.request, self.organizerPrincipal, self.uid))
         if calendar_resource:
             self.organizer_calendar = (yield calendar_resource.iCalendarForUser(self.request))
-        elif isinstance(self.organizerAddress, PartitionedCalendarUser):
+        elif type(self.organizerAddress) in (PartitionedCalendarUser, OtherServerCalendarUser,):
             # For partitioning where the organizer is on a different node, we will assume that the attendee's copy
             # of the event is up to date and "authoritative". So we pretend that is the organizer copy
             self.organizer_calendar = self.oldcalendar

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -42,8 +42,8 @@
 from twistedcaldav.scheduling.ischeduleservers import IScheduleServerRecord
 from twistedcaldav.scheduling.itip import iTIPRequestStatus
 from twistedcaldav.util import utf8String
-from twistedcaldav.scheduling.cuaddress import RemoteCalendarUser
-from twistedcaldav.scheduling.cuaddress import PartitionedCalendarUser
+from twistedcaldav.scheduling.cuaddress import PartitionedCalendarUser, RemoteCalendarUser,\
+    OtherServerCalendarUser
 
 import OpenSSL
 
@@ -86,6 +86,8 @@
                 server = servermgr.mapDomain(recipient.domain)
             elif isinstance(recipient, PartitionedCalendarUser):
                 server = self._getServerForPartitionedUser(recipient)
+            elif isinstance(recipient, OtherServerCalendarUser):
+                server = self._getServerForOtherServerUser(recipient)
             else:
                 assert False, "Incorrect calendar user address class"
             if not server:
@@ -132,13 +134,25 @@
         if not hasattr(self, "partitionedServers"):
             self.partitionedServers = {}
             
-        partition = recipient.principal.hostedURL()
+        partition = recipient.principal.partitionURI()
         if partition not in self.partitionedServers:
             self.partitionedServers[partition] = IScheduleServerRecord(uri=joinURL(partition, "/ischedule"))
             self.partitionedServers[partition].unNormalizeAddresses = False
         
         return self.partitionedServers[partition]
 
+    def _getServerForOtherServerUser(self, recipient):
+        
+        if not hasattr(self, "otherServers"):
+            self.otherServers = {}
+            
+        serverURI = recipient.principal.serverURI()
+        if serverURI not in self.otherServers:
+            self.otherServers[serverURI] = IScheduleServerRecord(uri=joinURL(serverURI, "/ischedule"))
+            self.otherServers[serverURI].unNormalizeAddresses = not recipient.principal.server().isImplicit
+        
+        return self.otherServers[serverURI]
+
 class IScheduleRequest(object):
     
     def __init__(self, scheduler, server, recipients, responses, refreshOnly=False):
@@ -165,10 +179,10 @@
                 proto = (yield ClientCreator(reactor, HTTPClientProtocol).connectTCP(self.server.host, self.server.port))
             
             request = ClientRequest("POST", self.server.path, self.headers, self.data)
-            yield log.logRequest("debug", "Sending server-to-server POST request:", request)
+            yield self.logRequest("debug", "Sending server-to-server POST request:", request)
             response = (yield proto.submitRequest(request))
     
-            yield log.logResponse("debug", "Received server-to-server POST response:", response)
+            yield self.logResponse("debug", "Received server-to-server POST response:", response)
             xml = (yield davXMLFromStream(response.stream))
     
             self._parseResponse(xml)
@@ -191,7 +205,7 @@
 
         assert level in logLevels
 
-        if self.willLogAtLevel(level):
+        if log.willLogAtLevel(level):
             iostr = StringIO()
             iostr.write("%s\n" % (message,))
             if hasattr(request, "clientproto"):
@@ -217,7 +231,7 @@
                 request.stream = MemoryStream(data if data is not None else "")
                 request.stream.doStartReading = None
             
-                self.emit(level, iostr.getvalue(), **kwargs)
+                log.emit(level, iostr.getvalue(), **kwargs)
 
             d = allDataFromStream(request.stream)
             d.addCallback(_gotData)
@@ -232,7 +246,7 @@
         """
         assert level in logLevels
 
-        if self.willLogAtLevel(level):
+        if log.willLogAtLevel(level):
             iostr = StringIO()
             iostr.write("%s\n" % (message,))
             code_message = responsecode.RESPONSES.get(response.code, "Unknown Status")
@@ -255,7 +269,7 @@
                 response.stream = MemoryStream(data if data is not None else "")
                 response.stream.doStartReading = None
             
-                self.emit(level, iostr.getvalue(), **kwargs)
+                log.emit(level, iostr.getvalue(), **kwargs)
                 
             d = allDataFromStream(response.stream)
             d.addCallback(_gotData)

Modified: CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -19,6 +19,7 @@
 import socket
 import urlparse
 
+from twisted.internet.abstract import isIPAddress
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.python.failure import Failure
 
@@ -38,7 +39,8 @@
 from twistedcaldav.ical import Component
 from twistedcaldav.scheduling import addressmapping
 from twistedcaldav.scheduling.caldav import ScheduleViaCalDAV
-from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser
+from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser,\
+    calendarUserFromPrincipal, OtherServerCalendarUser
 from twistedcaldav.scheduling.cuaddress import LocalCalendarUser
 from twistedcaldav.scheduling.cuaddress import RemoteCalendarUser
 from twistedcaldav.scheduling.cuaddress import EmailCalendarUser
@@ -47,6 +49,7 @@
 from twistedcaldav.scheduling.ischedule import ScheduleViaISchedule
 from twistedcaldav.scheduling.ischeduleservers import IScheduleServers
 from twistedcaldav.scheduling.itip import iTIPRequestStatus
+from twistedcaldav.servers import Servers
 
 """
 CalDAV/Server-to-Server scheduling behavior.
@@ -262,6 +265,8 @@
         # Parse the calendar object from the HTTP request stream
         try:
             self.calendar = (yield Component.fromIStream(self.request.stream))
+            
+            self.preProcessCalendarData()
             self.calendardata = str(self.calendar)
         except:
             # FIXME: Bare except
@@ -272,6 +277,13 @@
                 description="Can't parse calendar data"
             ))
 
+    def preProcessCalendarData(self):
+        """
+        After loading calendar data from the request, do some optional processing of it. This method will be
+        overridden by those schedulers that need to do special things to the data.
+        """
+        pass
+
     def checkAuthorization(self):
         raise NotImplementedError
 
@@ -451,6 +463,7 @@
         # Loop over each recipient and aggregate into lists by service types.
         caldav_recipients = []
         partitioned_recipients = []
+        otherserver_recipients = []
         remote_recipients = []
         imip_recipients = []
         for ctr, recipient in enumerate(self.recipients):
@@ -474,6 +487,9 @@
             elif isinstance(recipient, PartitionedCalendarUser):
                 partitioned_recipients.append(recipient)
 
+            elif isinstance(recipient, OtherServerCalendarUser):
+                otherserver_recipients.append(recipient)
+
             elif isinstance(recipient, RemoteCalendarUser):
                 remote_recipients.append(recipient)
 
@@ -496,6 +512,10 @@
         if partitioned_recipients:
             yield self.generateRemoteSchedulingResponses(partitioned_recipients, responses, freebusy, getattr(self.request, 'doing_attendee_refresh', False))
 
+        # Now process other server recipients
+        if otherserver_recipients:
+            yield self.generateRemoteSchedulingResponses(otherserver_recipients, responses, freebusy, getattr(self.request, 'doing_attendee_refresh', False))
+
         # To reduce chatter, we suppress certain messages
         if not getattr(self.request, 'suppressRefresh', False):
 
@@ -610,13 +630,11 @@
                 results.append(address)
             else:
                 # Map recipient to their inbox
-                inbox = None
                 inboxURL = principal.scheduleInboxURL()
-                if inboxURL:
-                    inbox = (yield self.request.locateResource(inboxURL)) if principal.locallyHosted() else "dummy"
+                inbox = (yield self.request.locateResource(inboxURL)) if principal.locallyHosted() else "dummy"
 
                 if inbox:
-                    results.append(LocalCalendarUser(recipient, principal, inbox, inboxURL) if principal.locallyHosted() else PartitionedCalendarUser(recipient, principal))
+                    results.append(calendarUserFromPrincipal(recipient, principal, inbox, inboxURL))
                 else:
                     log.err("No schedule inbox for principal: %s" % (principal,))
                     results.append(InvalidCalendarUser(recipient))
@@ -783,13 +801,11 @@
                 results.append(InvalidCalendarUser(recipient))
             else:
                 # Map recipient to their inbox
-                inbox = None
                 inboxURL = principal.scheduleInboxURL()
-                if inboxURL:
-                    inbox = (yield self.request.locateResource(inboxURL)) if principal.locallyHosted() else "dummy"
+                inbox = (yield self.request.locateResource(inboxURL)) if principal.locallyHosted() else "dummy"
 
                 if inbox:
-                    results.append(LocalCalendarUser(recipient, principal, inbox, inboxURL) if principal.locallyHosted() else PartitionedCalendarUser(recipient, principal))
+                    results.append(calendarUserFromPrincipal(recipient, principal, inbox, inboxURL))
                 else:
                     log.err("No schedule inbox for principal: %s" % (principal,))
                     results.append(InvalidCalendarUser(recipient))
@@ -807,6 +823,27 @@
         if self.request.headers.getRawHeaders('x-calendarserver-itip-refreshonly', ("F"))[0] == "T":
             self.request.doing_attendee_refresh = 1
         
+    def preProcessCalendarData(self):
+        """
+        For data coming in from outside we need to normalize the calendar user addresses so that later iTIP
+        processing will match calendar users against those in stored calendar data. Only do that for invites
+        not freebusy.
+        """
+
+        if not self.checkForFreeBusy():
+            def lookupFunction(cuaddr):
+                principal = self.resource.principalForCalendarUserAddress(cuaddr)
+                if principal is None:
+                    return (None, None, None)
+                else:
+                    return (
+                        principal.record.fullName.decode("utf-8"),
+                        principal.record.guid,
+                        principal.record.calendarUserAddresses
+                    )
+    
+            self.calendar.normalizeCalendarUserAddresses(lookupFunction)
+
     def checkAuthorization(self):
         # Must have an unauthenticated user
         if self.resource.currentPrincipal(self.request) != davxml.Principal(davxml.Unauthenticated()):
@@ -835,8 +872,8 @@
                     "Originator cannot be local to server",
                 ))
             else:
-                self.originator = PartitionedCalendarUser(self.originator, originatorPrincipal)
-                #self._validPartitionServer()
+                self.originator = calendarUserFromPrincipal(self.originator, originatorPrincipal)
+                self._validAlternateServer(originatorPrincipal)
         else:
             self.originator = RemoteCalendarUser(self.originator)
             self._validiScheduleServer()
@@ -897,34 +934,45 @@
                     "Originator not allowed to send to this server",
                 ))
 
-    def _validPartitionServer(self, principal):
+    def _validAlternateServer(self, principal):
         """
         Check the validity of the partitioned host.
         """
 
-        # Extract expected host/port
-        expected_uri = principal.hostedURL()
+        # Extract expected host/port. This will be the partitionURI, or if no partitions,
+        # the serverURI
+        expected_uri = principal.partitionURI()
+        if expected_uri is None:
+            expected_uri = principal.serverURI()
         expected_uri = urlparse.urlparse(expected_uri)
     
         # Get the request IP and map to hostname.
         clientip = self.request.remoteAddr.host
         
-        # First compare as dotted IP
+        # Check against this server (or any of its partitions). We need this because an external iTIP message
+        # may be addressed to users on different partitions, and the node receiving the iTIP message will need to
+        # forward it to the partition nodes, thus the client ip seen by the partitions will in fact be the initial
+        # receiving node.
         matched = False
-        if clientip == expected_uri.hostname:
+        if Servers.getThisServer().checkThisIP(clientip):
             matched = True
+    
+        # Next compare as dotted IP
+        elif isIPAddress(expected_uri.hostname):
+            if clientip == expected_uri.hostname:
+                matched = True
         else:
-            # Now do hostname lookup
+            # Now do expected hostname -> IP lookup
             try:
-                host, aliases, _ignore_ips = socket.gethostbyaddr(clientip)
-                for hostname in itertools.chain((host,), aliases):
-                    # Try host match
-                    if hostname == expected_uri.hostname:
+                # So now try the lookup of the expected host
+                _ignore_host, _ignore_aliases, ips = socket.gethostbyname_ex(expected_uri.hostname)
+                for ip in ips:
+                    if ip == clientip:
                         matched = True
                         break
             except socket.herror, e:
                 log.debug("iSchedule cannot lookup client ip '%s': %s" % (clientip, str(e),))
-        
+                
         if not matched:
             log.err("Originator not on allowed server: %s" % (self.originator,))
             raise HTTPError(ErrorResponse(
@@ -953,8 +1001,8 @@
                     ))
                 else:
                     # Check that the origin server is the correct partition
-                    self.organizer = PartitionedCalendarUser(organizer, organizerPrincipal)
-                    self._validPartitionServer(self.organizer.principal)
+                    self.organizer = calendarUserFromPrincipal(organizer, organizerPrincipal)
+                    self._validAlternateServer(self.organizer.principal)
             else:
                 localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(organizer))
                 if localUser:
@@ -992,7 +1040,7 @@
                     "Local attendee cannot send to this server",
                 ))
             else:
-                self._validPartitionServer(attendeePrincipal)                
+                self._validAlternateServer(attendeePrincipal)
         else:
             localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.attendee))
             if localUser:

Copied: CalendarServer/trunk/twistedcaldav/servers.py (from rev 7377, CalendarServer/branches/users/cdaboo/pods/twistedcaldav/servers.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/servers.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/servers.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -0,0 +1,229 @@
+##
+# Copyright (c) 2011 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 twext.python.log import Logger
+
+from twistedcaldav.client.pool import installPool
+from twistedcaldav.config import config, fullServerPath
+from twistedcaldav.xmlutil import readXML
+import urlparse
+import socket
+
+"""
+XML based server configuration file handling.
+
+This is used in an environment where more than one server is being used within a single domain. i.e., all
+the principals across the whole domain need to be able to directly schedule each other and know of each others
+existence. A common scenario would be a production server and a development/test server.
+
+Each server is identified by an id and url. The id is used when assigning principals to a specific server. Each
+server can also support multiple partitions, and each of those is identified by an id and url, with the id also
+being used to assign principals to a specific partition.
+"""
+
+__all__ = [
+    "Servers",
+]
+
+log = Logger()
+
+class ServersDB(object):
+    """
+    Represents the set of servers within the same domain.
+    """
+    
+    def __init__(self):
+        
+        self._servers = {}
+        self._xmlFile = None
+        self._thisServer = None
+
+    def load(self, xmlFile=None):
+        if self._xmlFile is None or xmlFile is not None:
+            self._servers = {}
+            if xmlFile:
+                self._xmlFile = xmlFile
+            else:
+                self._xmlFile = fullServerPath(
+                    config.ConfigRoot,
+                    config.Servers.ConfigFile
+                )
+        self._servers = ServersParser.parse(self._xmlFile)
+        for server in self._servers.values():
+            if server.thisServer:
+                self._thisServer = server
+                break
+        else:
+            raise ValueError("No server in %s matches this server." % (self._xmlFile,))
+    
+    def clear(self):
+        self._servers = {}
+        self._xmlFile = None
+        self._thisServer = None
+
+    def getServerById(self, id):
+        return self._servers.get(id)
+        
+    def getServerURIById(self, id):
+        try:
+            return self._servers[id].uri
+        except KeyError:
+            return None
+    
+    def getThisServer(self):
+        return self._thisServer
+
+Servers = ServersDB()   # Global server DB
+
+class Server(object):
+    """
+    Represents a server which may itself be partitioned.
+    """
+    
+    def __init__(self):
+        self.id = None
+        self.uri = None
+        self.thisServer = False
+        self.ips = set()
+        self.partitions = {}
+        self.partitions_ips = set()
+        self.isImplicit = True
+    
+    def check(self):
+        # Check whether this matches the current server
+        parsed_uri = urlparse.urlparse(self.uri)
+        if parsed_uri.hostname == config.ServerHostName:
+            if parsed_uri.scheme == "http":
+                if config.HTTPPort:
+                    self.thisServer = parsed_uri.port in (config.HTTPPort,) + tuple(config.BindHTTPPorts)
+            elif parsed_uri.scheme == "https":
+                if config.SSLPort:
+                    self.thisServer = parsed_uri.port in (config.SSLPort,) + tuple(config.BindSSLPorts)
+        
+        # Need to cache IP addresses
+        try:
+            _ignore_host, _ignore_aliases, ips = socket.gethostbyname_ex(parsed_uri.hostname)
+        except socket.gaierror, e:
+            log.error("Unable to lookup ip-addr for server '%s': %s" % (parsed_uri.hostname, str(e)))
+            ips = ()
+        self.ips = set(ips)
+
+        for uri in self.partitions.values():
+            parsed_uri = urlparse.urlparse(uri)
+            try:
+                _ignore_host, _ignore_aliases, ips = socket.gethostbyname_ex(parsed_uri.hostname)
+            except socket.gaierror, e:
+                log.error("Unable to lookup ip-addr for partition '%s': %s" % (parsed_uri.hostname, str(e)))
+                ips = ()
+            self.partitions_ips.update(ips)
+    
+    def checkThisIP(self, ip):
+        """
+        Check that the passed in IP address corresponds to this server or one of its partitions.
+        """
+        return (ip in self.ips) or (ip in self.partitions_ips)
+
+    def addPartition(self, id, uri):
+        self.partitions[id] = uri
+    
+    def getPartitionURIForId(self, id):
+        return self.partitions.get(id)
+    
+    def installReverseProxies(self, ownUID, maxClients):
+        
+        for partition, url in self.partitions.iteritems():
+            if partition != ownUID:
+                installPool(
+                    partition,
+                    url,
+                    maxClients,
+                )
+    
+        
+        
+ELEMENT_SERVERS                 = "servers"
+ELEMENT_SERVER                  = "server"
+ELEMENT_ID                      = "id"
+ELEMENT_URI                     = "uri"
+ELEMENT_PARTITIONS              = "partitions"
+ELEMENT_PARTITION               = "partition"
+ATTR_IMPLICIT                   = "implicit"
+ATTR_VALUE_YES                  = "yes"
+ATTR_VALUE_NO                   = "no"
+
+class ServersParser(object):
+    """
+    Servers configuration file parser.
+    """
+    @staticmethod
+    def parse(xmlFile):
+
+        results = {}
+
+        # Read in XML
+        try:
+            _ignore_tree, servers_node = readXML(xmlFile, ELEMENT_SERVERS)
+        except ValueError, e:
+            log.error("XML parse error for '%s' because: %s" % (xmlFile, e,), raiseException=RuntimeError)
+
+        for child in servers_node.getchildren():
+            
+            if child.tag != ELEMENT_SERVER:
+                log.error("Unknown server type: '%s' in servers file: '%s'" % (child.tag, xmlFile,), raiseException=RuntimeError)
+
+            server = Server()
+            server.isImplicit = child.get(ATTR_IMPLICIT, ATTR_VALUE_YES) == ATTR_VALUE_YES
+
+            for node in child.getchildren():
+                if node.tag == ELEMENT_ID:
+                    server.id = node.text
+                elif node.tag == ELEMENT_URI:
+                    server.uri = node.text
+                elif node.tag == ELEMENT_PARTITIONS:
+                    ServersParser._parsePartition(xmlFile, node, server)
+                else:
+                    log.error("Invalid element '%s' in servers file: '%s'" % (node.tag, xmlFile,), raiseException=RuntimeError)
+
+            if server.id is None or server.uri is None:
+                log.error("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,), raiseException=RuntimeError)
+
+            server.check()
+            results[server.id] = server
+
+        return results
+
+    @staticmethod
+    def _parsePartition(xmlFile, partitions, server):
+
+        for child in partitions.getchildren():
+            
+            if child.tag != ELEMENT_PARTITION:
+                log.error("Unknown partition type: '%s' in servers file: '%s'" % (child.tag, xmlFile,), raiseException=RuntimeError)
+
+            id = None
+            uri = None
+            for node in child.getchildren():
+                if node.tag == ELEMENT_ID:
+                    id = node.text
+                elif node.tag == ELEMENT_URI:
+                    uri = node.text
+                else:
+                    log.error("Invalid element '%s' in augment file: '%s'" % (node.tag, xmlFile,), raiseException=RuntimeError)
+        
+            if id is None or uri is None:
+                log.error("Invalid partition '%s' in servers file: '%s'" % (child.tag, xmlFile,), raiseException=RuntimeError)
+            
+            server.addPartition(id, uri)

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -15,8 +15,10 @@
 # limitations under the License.
 ##
 
+from socket import getfqdn
+from socket import gethostbyname
+import copy
 import os
-import copy
 import re
 
 from twext.web2.dav import davxml
@@ -29,7 +31,6 @@
 from twistedcaldav import caldavxml, customxml, carddavxml
 from twistedcaldav.config import ConfigProvider, ConfigurationError
 from twistedcaldav.config import config, _mergeData, fullServerPath
-from twistedcaldav.partitions import partitions
 from twistedcaldav.util import getPasswordFromKeychain
 from twistedcaldav.util import KeychainAccessError, KeychainPasswordNotFound
 
@@ -120,6 +121,14 @@
         "xmlFile": "resources.xml",
         "recordTypes" : ("locations", "resources"),
     },
+    "twistedcaldav.directory.appleopendirectory.OpenDirectoryService": {
+        "node": "/Search",
+        "cacheTimeout": 1,
+        "negativeCaching": False,
+        "restrictEnabledRecords": False,
+        "restrictToGroup": "",
+        "recordTypes" : ("locations", "resources"),
+    },
 }
 
 DEFAULT_AUGMENT_PARAMS = {
@@ -147,6 +156,7 @@
         "database": "proxies",
         "user":     "",
         "password": "",
+        "dbtype": "",
     },
 }
 
@@ -587,14 +597,14 @@
     },
 
     #
-    # Partitioning
+    # Support multiple hosts within a domain
     #
-    "Partitioning" : {
-        "Enabled": False,                          # Partitioning enabled or not
-        "ServerPartitionID": "",                   # Unique ID for this server's partition instance.
-        "PartitionConfigFile": "partitions.plist", # File path for partition information
+    "Servers" : {
+        "Enabled": False,                          # Multiple servers/partitions enabled or not
+        "ConfigFile": "servers.xml",               # File path for server information
         "MaxClients": 5,                           # Pool size for connections to each partition
     },
+    "ServerPartitionID": "",                       # Unique ID for this server's partition instance.
 
     #
     # Performance tuning
@@ -758,10 +768,10 @@
         if "Includes" in configDict:
             configRoot = os.path.join(configDict.ServerRoot, configDict.ConfigRoot)
             for include in configDict.Includes:
-
-                additionalDict = self._parseConfigFromFile(fullServerPath(configRoot, include))
+                path = _expandPath(fullServerPath(configRoot, include))
+                additionalDict = self._parseConfigFromFile(path)
                 if additionalDict:
-                    log.info("Adding configuration from file: '%s'" % (include,))
+                    log.info("Adding configuration from file: '%s'" % (path,))
                     configDict.update(additionalDict)
         return configDict
 
@@ -778,6 +788,13 @@
             configDict = _cleanup(configDict, self._defaults)
         return configDict
 
+def _expandPath(path):
+    if '$' in path:
+        return path.replace('$', getfqdn())
+    elif '#' in path:
+        return path.replace('#', gethostbyname(getfqdn()))
+    else:
+        return path
 
 RELATIVE_PATHS = [
     ("ServerRoot", "DataRoot"),
@@ -849,7 +866,6 @@
 
 def _updateHostName(configDict):
     if not configDict.ServerHostName:
-        from socket import getfqdn
         hostname = getfqdn()
         if not hostname:
             hostname = "localhost"
@@ -881,7 +897,33 @@
             if param not in DEFAULT_SERVICE_PARAMS[configDict.DirectoryService.type]:
                 del configDict.DirectoryService.params[param]
 
+def _preUpdateResourceService(configDict, items):
+    # Special handling for directory services configs
+    dsType = items.get("ResourceService", {}).get("type", None)
+    if dsType is None:
+        dsType = configDict.ResourceService.type
+    else:
+        if dsType == configDict.ResourceService.type:
+            oldParams = configDict.ResourceService.params
+            newParams = items.ResourceService.get("params", {})
+            _mergeData(oldParams, newParams)
+        else:
+            if dsType in DEFAULT_RESOURCE_PARAMS:
+                configDict.ResourceService.params = copy.deepcopy(DEFAULT_RESOURCE_PARAMS[dsType])
+            else:
+                configDict.ResourceService.params = {}
 
+    for param in items.get("ResourceService", {}).get("params", {}):
+        if dsType in DEFAULT_RESOURCE_PARAMS and param not in DEFAULT_RESOURCE_PARAMS[dsType]:
+            log.warn("Parameter %s is not supported by service %s" % (param, dsType))
+            
+def _postUpdateResourceService(configDict):
+    if configDict.ResourceService.type in DEFAULT_RESOURCE_PARAMS:
+        for param in tuple(configDict.ResourceService.params):
+            if param not in DEFAULT_RESOURCE_PARAMS[configDict.ResourceService.type]:
+                del configDict.ResourceService.params[param]
+
+
 def _preUpdateDirectoryAddressBookBackingDirectoryService(configDict, items):
     #
     # Special handling for directory address book configs
@@ -1116,14 +1158,16 @@
                     log.info("iMIP %s password not found in keychain" %
                         (direction,))
 
-def _updatePartitions(configDict):
-    if configDict.Partitioning.Enabled:
-        partitions.setSelfPartition(configDict.Partitioning.ServerPartitionID)
-        partitions.setMaxClients(configDict.Partitioning.MaxClients)
-        partitions.readConfig(fullServerPath(configDict.ConfigRoot, configDict.Partitioning.PartitionConfigFile))
-        partitions.installReverseProxies()
+def _updateServers(configDict):
+    import servers
+    if configDict.Servers.Enabled:
+        servers.Servers.load()
+        servers.Servers.getThisServer().installReverseProxies(
+            configDict.ServerPartitionID,
+            configDict.Servers.MaxClients,
+        )
     else:
-        partitions.clear()
+        servers.Servers.clear()
 
 def _updateCompliance(configDict):
 
@@ -1156,12 +1200,14 @@
 
 PRE_UPDATE_HOOKS = (
     _preUpdateDirectoryService,
+    _preUpdateResourceService,
     _preUpdateDirectoryAddressBookBackingDirectoryService,
     )
 POST_UPDATE_HOOKS = (
     _updateDataStore,
     _updateHostName,
     _postUpdateDirectoryService,
+    _postUpdateResourceService,
     _postUpdateAugmentService,
     _postUpdateProxyDBService,
     _updateACLs,
@@ -1169,7 +1215,7 @@
     _updateLogLevels,
     _updateNotifications,
     _updateScheduling,
-    _updatePartitions,
+    _updateServers,
     _updateCompliance,
     )
     

Modified: CalendarServer/trunk/twistedcaldav/test/test_config.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_config.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/test/test_config.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -22,6 +22,7 @@
 from twistedcaldav.stdconfig import DEFAULT_CONFIG, PListConfigProvider,\
     RELATIVE_PATHS
 from twistedcaldav.test.util import TestCase
+import socket
 
 testConfig = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -378,3 +379,194 @@
         # But attr syntax is OK
         configDict._x = "X"
         self.assertEquals(configDict._x, "X")
+
+    def test_SimpleInclude(self):
+
+        testConfigMaster = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>ResponseCompression</key>
+  <false/>
+
+  <key>ServerRoot</key>
+  <string></string>
+
+  <key>ConfigRoot</key>
+  <string></string>
+
+  <key>HTTPPort</key>
+  <integer>8008</integer>
+
+  <key>SSLPort</key>
+  <integer>8443</integer>
+
+  <key>DefaultLogLevel</key>
+  <string>info</string>
+  <key>LogLevels</key>
+  <dict>
+    <key>some.namespace</key>
+    <string>debug</string>
+  </dict>
+  
+  <key>Includes</key>
+  <array>
+      <string>%s</string>
+  </array>
+
+</dict>
+</plist>
+"""
+
+        testConfigInclude = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>HTTPPort</key>
+  <integer>9008</integer>
+
+</dict>
+</plist>
+"""
+
+        config.setProvider(PListConfigProvider(DEFAULT_CONFIG))
+
+        self.testInclude = self.mktemp()
+        open(self.testInclude, "w").write(testConfigInclude)
+
+        self.testMaster = self.mktemp()
+        open(self.testMaster, "w").write(testConfigMaster % (self.testInclude,))
+
+        config.load(self.testMaster)
+        self.assertEquals(config.HTTPPort, 9008)
+        self.assertEquals(config.SSLPort, 8443)
+
+    def test_FQDNInclude(self):
+
+        testConfigMaster = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>ResponseCompression</key>
+  <false/>
+
+  <key>ServerRoot</key>
+  <string></string>
+
+  <key>ConfigRoot</key>
+  <string></string>
+
+  <key>HTTPPort</key>
+  <integer>8008</integer>
+
+  <key>SSLPort</key>
+  <integer>8443</integer>
+
+  <key>DefaultLogLevel</key>
+  <string>info</string>
+  <key>LogLevels</key>
+  <dict>
+    <key>some.namespace</key>
+    <string>debug</string>
+  </dict>
+  
+  <key>Includes</key>
+  <array>
+      <string>%s.$</string>
+  </array>
+
+</dict>
+</plist>
+"""
+
+        testConfigInclude = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>HTTPPort</key>
+  <integer>9008</integer>
+
+</dict>
+</plist>
+"""
+
+        config.setProvider(PListConfigProvider(DEFAULT_CONFIG))
+
+        self.testIncludeRoot = self.mktemp()
+        self.testInclude = self.testIncludeRoot + "." + socket.getfqdn()
+        open(self.testInclude, "w").write(testConfigInclude)
+
+        self.testMaster = self.mktemp()
+        open(self.testMaster, "w").write(testConfigMaster % (self.testIncludeRoot,))
+
+        config.load(self.testMaster)
+        self.assertEquals(config.HTTPPort, 9008)
+        self.assertEquals(config.SSLPort, 8443)
+
+    def test_HostnameInclude(self):
+
+        testConfigMaster = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>ResponseCompression</key>
+  <false/>
+
+  <key>ServerRoot</key>
+  <string></string>
+
+  <key>ConfigRoot</key>
+  <string></string>
+
+  <key>HTTPPort</key>
+  <integer>8008</integer>
+
+  <key>SSLPort</key>
+  <integer>8443</integer>
+
+  <key>DefaultLogLevel</key>
+  <string>info</string>
+  <key>LogLevels</key>
+  <dict>
+    <key>some.namespace</key>
+    <string>debug</string>
+  </dict>
+  
+  <key>Includes</key>
+  <array>
+      <string>%s.#</string>
+  </array>
+
+</dict>
+</plist>
+"""
+
+        testConfigInclude = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+
+  <key>HTTPPort</key>
+  <integer>9008</integer>
+
+</dict>
+</plist>
+"""
+
+        config.setProvider(PListConfigProvider(DEFAULT_CONFIG))
+
+        self.testIncludeRoot = self.mktemp()
+        self.testInclude = self.testIncludeRoot + "." + socket.gethostbyname(socket.getfqdn())
+        open(self.testInclude, "w").write(testConfigInclude)
+
+        self.testMaster = self.mktemp()
+        open(self.testMaster, "w").write(testConfigMaster % (self.testIncludeRoot,))
+
+        config.load(self.testMaster)
+        self.assertEquals(config.HTTPPort, 9008)
+        self.assertEquals(config.SSLPort, 8443)

Copied: CalendarServer/trunk/twistedcaldav/test/test_servers.py (from rev 7377, CalendarServer/branches/users/cdaboo/pods/twistedcaldav/test/test_servers.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_servers.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/test/test_servers.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -0,0 +1,89 @@
+##
+# Copyright (c) 2009-2010 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 twistedcaldav.servers import Servers
+from twistedcaldav.test.util import TestCase
+import StringIO as StringIO
+from twistedcaldav.config import config
+
+class ServerTests(TestCase):
+
+    data1 = """<?xml version="1.0" encoding="utf-8"?>
+<servers>
+  <server>
+    <id>00001</id>
+    <uri>http://caldav1.example.com:8008</uri>
+  </server>
+  <server>
+    <id>00002</id>
+    <uri>https://caldav2.example.com:8843</uri>
+    <partitions>
+        <partition>
+            <id>A</id>
+            <uri>https://machine1.example.com:8443</uri>
+        </partition>
+        <partition>
+            <id>B</id>
+            <uri>https://machine2.example.com:8443</uri>
+        </partition>
+    </partitions>
+  </server>
+</servers>
+"""
+        
+    def test_read_ok(self):
+        
+        self.patch(config, "ServerHostName", "caldav1.example.com")
+        self.patch(config, "HTTPPort", 8008)
+
+        xmlFile = StringIO.StringIO(ServerTests.data1)
+        servers = Servers
+        servers.load(xmlFile)
+
+        self.assertTrue(servers.getServerById("00001") is not None)
+        self.assertTrue(servers.getServerById("00002") is not None)
+
+        self.assertEqual(servers.getServerById("00001").uri, "http://caldav1.example.com:8008")
+        self.assertEqual(servers.getServerById("00002").uri, "https://caldav2.example.com:8843")
+
+        self.assertEqual(len(servers.getServerById("00001").partitions), 0)
+        self.assertEqual(len(servers.getServerById("00002").partitions), 2)
+
+        self.assertEqual(servers.getServerById("00002").getPartitionURIForId("A"), "https://machine1.example.com:8443")
+        self.assertEqual(servers.getServerById("00002").getPartitionURIForId("B"), "https://machine2.example.com:8443")
+
+    def test_this_server(self):
+        
+        self.patch(config, "ServerHostName", "caldav1.example.com")
+        self.patch(config, "HTTPPort", 8008)
+        
+        xmlFile = StringIO.StringIO(ServerTests.data1)
+        servers = Servers
+        servers.load(xmlFile)
+
+        self.assertTrue(servers.getServerById("00001").thisServer)
+        self.assertFalse(servers.getServerById("00002").thisServer)
+        
+        self.patch(config, "ServerHostName", "caldav2.example.com")
+        self.patch(config, "SSLPort", 8443)
+        self.patch(config, "BindSSLPorts", [8843])
+        
+        xmlFile = StringIO.StringIO(ServerTests.data1)
+        servers = Servers
+        servers.load(xmlFile)
+
+        self.assertFalse(servers.getServerById("00001").thisServer)
+        self.assertTrue(servers.getServerById("00002").thisServer)

Modified: CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_upgrade.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/test/test_upgrade.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -30,6 +30,7 @@
 
 import hashlib
 import os, zlib, cPickle
+from txdav.caldav.datastore.index_file import db_basename
 
 
 
@@ -290,7 +291,11 @@
             "calendars": {
                 "users": {
                     "wsanchez": {
-                        "calendar" : {},
+                        "calendar" : {
+                            db_basename : {
+                                "@contents": "",
+                            },
+                         },
                         "notifications": {
                             "sample-notification.xml": {
                                 "@contents":  "<?xml version='1.0'>\n<should-be-ignored />"
@@ -307,7 +312,11 @@
                     "64" : {
                         "23" : {
                             "6423F94A-6B76-4A3A-815B-D52CFD77935D" : {
-                                "calendar": {},
+                                "calendar": {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
+                                },
                             }
                         }
                     }
@@ -342,6 +351,9 @@
                     {
                         "calendar" :
                         {
+                            db_basename : {
+                                "@contents": "",
+                            },
                             "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                             {
                                 "@contents" : event01_before,
@@ -357,6 +369,9 @@
                         },
                         "inbox" :
                         {
+                            db_basename : {
+                                "@contents": "",
+                            },
                             "@xattrs" :
                             {
                                 # Pickled XML Doc
@@ -371,6 +386,9 @@
                     {
                         "calendar" :
                         {
+                            db_basename : {
+                                "@contents": "",
+                            },
                         },
                     },
                 },
@@ -407,6 +425,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,
@@ -422,6 +443,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
@@ -438,6 +462,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                 },
                             },
                         },
@@ -645,13 +672,16 @@
                 "23" : {
                     "6423F94A-6B76-4A3A-815B-D52CFD77935D" : {
                         "calendar" : {
+                            db_basename : {
+                                "@contents": "",
+                            },
                         },
                         "garbage.ics" : {
                             "@contents": "Oops, not actually an ICS file.",
                         },
                         "other-file.txt": {
                             "@contents": "Also not a calendar collection."
-                        }
+                        },
                     }
                 }
             },
@@ -727,6 +757,122 @@
 
 
     @inlineCallbacks
+    def test_calendarsUpgradeWithNestedCollections(self):
+        """
+        Unknown files, including .DS_Store files at any point in the hierarchy,
+        as well as non-directory in a user's calendar home, will be ignored and not
+        interrupt an upgrade.
+        """
+
+        self.setUpXMLDirectory()
+
+        beforeUIDContents = {
+            "64" : {
+                "23" : {
+                    "6423F94A-6B76-4A3A-815B-D52CFD77935D" : {
+                        "calendar" : {
+                            db_basename : {
+                                "@contents": "",
+                            },
+                        },
+                        "nested1": {
+                            "nested2": {},
+                        },
+                    }
+                }
+            },
+            ".DS_Store" : {
+                "@contents" : "",
+            }
+        }
+
+        afterUIDContents = {
+            "64" : {
+                "23" : {
+                    "6423F94A-6B76-4A3A-815B-D52CFD77935D" : {
+                        "calendar" : {
+                            db_basename : {
+                                "@contents": "",
+                            },
+                        },
+                        ".collection.nested1": {
+                            "nested2": {},
+                        },
+                    }
+                }
+            },
+            ".DS_Store" : {
+                "@contents" : "",
+            }
+        }
+
+        before = {
+            ".DS_Store" :
+            {
+                "@contents" : "",
+            },
+            "calendars" :
+            {
+                ".DS_Store" :
+                {
+                    "@contents" : "",
+                },
+                "__uids__" :beforeUIDContents,
+            },
+            "principals" :
+            {
+                ".DS_Store" :
+                {
+                    "@contents" : "",
+                },
+                OLDPROXYFILE :
+                {
+                    "@contents" : "",
+                }
+            }
+        }
+
+        after = {
+            ".DS_Store" :
+            {
+                "@contents" : "",
+            },
+            "tasks" :
+            {
+                "incoming" :
+                {
+                },
+            },
+            ".calendarserver_version" :
+            {
+                "@contents" : "2",
+            },
+            "calendars" :
+            {
+                ".DS_Store" :
+                {
+                    "@contents" : "",
+                },
+                "__uids__" : afterUIDContents,
+            },
+            NEWPROXYFILE :
+            {
+                "@contents" : None,
+            },
+            MailGatewayTokensDatabase.dbFilename :
+            {
+                "@contents" : None,
+            },
+            "%s-journal" % (MailGatewayTokensDatabase.dbFilename,) :
+            {
+                "@contents" : None
+            },
+        }
+
+        (yield self.verifyDirectoryComparison(before, after, reverify=True))
+
+
+    @inlineCallbacks
     def test_calendarsUpgradeWithUIDs(self):
         """
         Verify that calendar homes in the /calendars/__uids__/<guid>/ form
@@ -744,6 +890,9 @@
                     {
                         "calendar" :
                         {
+                            db_basename : {
+                                "@contents": "",
+                            },
                             "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                             {
                                 "@contents" : event01_before,
@@ -751,6 +900,9 @@
                         },
                         "inbox" :
                         {
+                            db_basename : {
+                                "@contents": "",
+                            },
                             "@xattrs" :
                             {
                                 # Plain XML
@@ -792,6 +944,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,
@@ -803,6 +958,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
@@ -852,6 +1010,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_before,
@@ -868,6 +1029,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         # Zlib compressed XML
@@ -908,6 +1072,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,
@@ -924,6 +1091,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
@@ -972,6 +1142,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,
@@ -988,6 +1161,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         # Zlib compressed XML
@@ -1028,6 +1204,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,
@@ -1044,6 +1223,9 @@
                                 },
                                 "inbox" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "@xattrs" :
                                     {
                                         freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"),
@@ -1093,6 +1275,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_before,
@@ -1133,6 +1318,9 @@
                             {
                                 "calendar" :
                                 {
+                                    db_basename : {
+                                        "@contents": "",
+                                    },
                                     "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
                                     {
                                         "@contents" : event01_after,

Modified: CalendarServer/trunk/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/upgrade.py	2011-04-27 21:09:24 UTC (rev 7377)
+++ CalendarServer/trunk/twistedcaldav/upgrade.py	2011-04-28 01:17:23 UTC (rev 7378)
@@ -38,6 +38,8 @@
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, succeed
 
+from txdav.caldav.datastore.index_file import db_basename
+
 from calendarserver.tools.util import getDirectory
 from calendarserver.tools.resources import migrateResources
 from twisted.python.reflect import namedAny
@@ -471,59 +473,151 @@
 
     return succeed(None)
 
-
+ at inlineCallbacks
 def upgrade_to_2(config):
-    #
-    # Rename proxy DB
-    #
-    oldFilename = "calendaruserproxy.sqlite"
-    newFilename = "proxies.sqlite"
+    
+    errorOccurred = False
 
-    oldDbPath = os.path.join(config.DataRoot, oldFilename)
-    newDbPath = os.path.join(config.DataRoot, newFilename)
-    if os.path.exists(oldDbPath) and not os.path.exists(newDbPath):
-        os.rename(oldDbPath, newDbPath)
+    def renameProxyDB():
+        #
+        # Rename proxy DB
+        #
+        oldFilename = "calendaruserproxy.sqlite"
+        newFilename = "proxies.sqlite"
+    
+        oldDbPath = os.path.join(config.DataRoot, oldFilename)
+        newDbPath = os.path.join(config.DataRoot, newFilename)
+        if os.path.exists(oldDbPath) and not os.path.exists(newDbPath):
+            os.rename(oldDbPath, newDbPath)
 
-    #
-    # Migrates locations and resources from OD
-    #
-    triggerFile = "trigger_resource_migration"
-    triggerPath = os.path.join(config.ServerRoot, triggerFile)
-    if os.path.exists(triggerPath):
-        os.remove(triggerPath)
+    def migrateFromOD():
+        #
+        # Migrates locations and resources from OD
+        #
+        triggerFile = "trigger_resource_migration"
+        triggerPath = os.path.join(config.ServerRoot, triggerFile)
+        if os.path.exists(triggerPath):
+            os.remove(triggerPath)
+    
+            log.info("Migrating locations and resources")
+    
+            directory = getDirectory()
+            userService = directory.serviceForRecordType("users")
+            resourceService = directory.serviceForRecordType("resources")
+            if (
+                not isinstance(userService, OpenDirectoryService) or
+                not isinstance(resourceService, XMLDirectoryService)
+            ):
+                # Configuration requires no migration
+                return succeed(None)
+    
+            # Fetch the autoSchedule assignments from resourceinfo.sqlite and pass
+            # those to migrateResources
+            autoSchedules = {}
+            dbPath = os.path.join(config.DataRoot, ResourceInfoDatabase.dbFilename)
+            if os.path.exists(dbPath):
+                resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
+                results = resourceInfoDatabase._db_execute(
+                    "select GUID, AUTOSCHEDULE from RESOURCEINFO"
+                )
+                for guid, autoSchedule in results:
+                    autoSchedules[guid] = autoSchedule
+    
+            # Create internal copies of resources and locations based on what is
+            # found in OD, overriding the autoSchedule default with existing
+            # assignments from resourceinfo.sqlite
+            return migrateResources(userService, resourceService,
+                autoSchedules=autoSchedules)
 
-        log.info("Migrating locations and resources")
+    def flattenHome(calHome):
 
-        directory = getDirectory()
-        userService = directory.serviceForRecordType("users")
-        resourceService = directory.serviceForRecordType("resources")
-        if (
-            not isinstance(userService, OpenDirectoryService) or
-            not isinstance(resourceService, XMLDirectoryService)
-        ):
-            # Configuration requires no migration
-            return succeed(None)
+        log.debug("Flattening calendar home: %s" % (calHome,))
 
-        # Fetch the autoSchedule assignments from resourceinfo.sqlite and pass
-        # those to migrateResources
-        autoSchedules = {}
-        dbPath = os.path.join(config.DataRoot, ResourceInfoDatabase.dbFilename)
-        if os.path.exists(dbPath):
-            resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
-            results = resourceInfoDatabase._db_execute(
-                "select GUID, AUTOSCHEDULE from RESOURCEINFO"
-            )
-            for guid, autoSchedule in results:
-                autoSchedules[guid] = autoSchedule
+        try:
+            for cal in os.listdir(calHome):
+                calPath = os.path.join(calHome, cal)
+                if not os.path.isdir(calPath):
+                    # Skip non-directories; these might have been uploaded by a
+                    # random DAV client, they can't be calendar collections.
+                    continue
+                
+                if cal in ("dropbox",):
+                    continue
+                if os.path.exists(os.path.join(calPath, db_basename)):
+                    continue
+                
+                # Commented this out because it is only needed if there are calendars nested inside of regular collections.
+                # Whilst this is technically possible in early versions of the servers the main clients did not support it.
+                # Also, the v1 upgrade does not look at nested calendars for cu-address normalization.
+                # However, we do still need to "ignore" regular collections in the calendar home so what we do is rename them
+                # with a ".collection." prefix.
+#                def scanCollection(collection):
+#                    
+#                    for child in os.listdir(collection):
+#                        childCollection = os.path.join(collection, child)
+#                        if os.path.isdir(childCollection):
+#                            if os.path.exists(os.path.join(childCollection, db_basename)):
+#                                newPath = os.path.join(calHome, child)
+#                                if os.path.exists(newPath):
+#                                    newPath = os.path.join(calHome, str(uuid.uuid4()))
+#                                log.debug("Moving up calendar: %s" % (childCollection,))
+#                                os.rename(childCollection, newPath)
+#                            else:
+#                                scanCollection(childCollection)
 
-        # Create internal copies of resources and locations based on what is
-        # found in OD, overriding the autoSchedule default with existing
-        # assignments from resourceinfo.sqlite
-        return migrateResources(userService, resourceService,
-            autoSchedules=autoSchedules)
+                if os.path.isdir(calPath):
+                    #log.debug("Regular collection scan: %s" % (calPath,))
+                    #scanCollection(calPath)
+                    log.warn("Regular collection hidden: %s" % (calPath,))
+                    os.rename(calPath, os.path.join(calHome, ".collection." + os.path.basename(calPath)))
 
+        except Exception, e:
+            log.error("Failed to upgrade calendar home %s: %s" % (calHome, e))
+            return False
+        
+        return True
 
+    def flattenHomes():
+        """
+        Make sure calendars inside regular collections are all moved to the top level.
+        """
+        errorOccurred = False
+    
+        log.debug("Flattening calendar homes")
 
+        docRoot = config.DocumentRoot
+        if os.path.exists(docRoot):
+            calRoot = os.path.join(docRoot, "calendars")
+            if os.path.exists(calRoot) and os.path.isdir(calRoot):
+                uidHomes = os.path.join(calRoot, "__uids__")
+                if os.path.isdir(uidHomes): 
+                    for path1 in os.listdir(uidHomes):
+                        uidLevel1 = os.path.join(uidHomes, path1)
+                        if not os.path.isdir(uidLevel1):
+                            continue
+                        for path2 in os.listdir(uidLevel1):
+                            uidLevel2 = os.path.join(uidLevel1, path2)
+                            if not os.path.isdir(uidLevel2):
+                                continue
+                            for home in os.listdir(uidLevel2):
+                                calHome = os.path.join(uidLevel2, home)
+                                if not os.path.isdir(calHome):
+                                    continue
+                                if not flattenHome(calHome):
+                                    errorOccurred = True
+        
+        return errorOccurred
+        
+    renameProxyDB()
+    errorOccurred = flattenHomes()
+    try:
+        yield migrateFromOD()
+    except:
+        errorOccurred = True
+        
+    if errorOccurred:
+        raise UpgradeError("Data upgrade failed, see error.log for details")
+    
 
 # The on-disk version number (which defaults to zero if .calendarserver_version
 # doesn't exist), is compared with each of the numbers in the upgradeMethods


Property changes on: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110427/6173d146/attachment-0001.html>


More information about the calendarserver-changes mailing list