[CalendarServer-changes] [15165] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Oct 5 14:18:27 PDT 2015


Revision: 15165
          http://trac.calendarserver.org//changeset/15165
Author:   sagen at apple.com
Date:     2015-10-05 14:18:27 -0700 (Mon, 05 Oct 2015)
Log Message:
-----------
Provide defaults for LDAP mappings

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/push/amppush.py
    CalendarServer/trunk/conf/caldavd-apple.plist
    CalendarServer/trunk/conf/caldavd-test.plist
    CalendarServer/trunk/contrib/performance/loadtest/clients.plist
    CalendarServer/trunk/contrib/performance/loadtest/config.plist
    CalendarServer/trunk/contrib/performance/loadtest/ical.py
    CalendarServer/trunk/contrib/performance/loadtest/population.py
    CalendarServer/trunk/contrib/performance/loadtest/profiles.py
    CalendarServer/trunk/contrib/performance/loadtest/sim.py
    CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
    CalendarServer/trunk/contrib/performance/loadtest/test_sim.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py

Added Paths:
-----------
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/Profile
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/StartupProfile
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/notification_multiget_report_hrefs.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_depth1_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_notification_depth1_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/post_freebusy.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/principal_search_report.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/report_principal_search.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_color_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_description_proppatch.request.xml
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_displayname_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_order_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_timezone_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_transparent_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_date_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_datetime_proppatch.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_create_calendar.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_delegate_principal_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_expand.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_initial_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_propfind.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principals_report.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_query_events_depth1_report.request
    CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_well_known_propfind.request

Modified: CalendarServer/trunk/calendarserver/push/amppush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/amppush.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/calendarserver/push/amppush.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -42,7 +42,7 @@
 
 
 class UnsubscribeFromID(amp.Command):
-    arguments = [('token', amp.String()), ('id', amp.String())]
+    arguments = [('id', amp.String())]
     response = [('status', amp.String())]
 
 
@@ -257,7 +257,7 @@
         return {"status" : "OK"}
     SubscribeToID.responder(subscribe)
 
-    def unsubscribe(self, token, id):
+    def unsubscribe(self, id):
         try:
             del self.subscriptions[id]
         except KeyError:

Modified: CalendarServer/trunk/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-apple.plist	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/conf/caldavd-apple.plist	2015-10-05 21:18:27 UTC (rev 15165)
@@ -371,10 +371,10 @@
       -->
 
     <key>UserName</key>
-    <string>calendar</string>
+    <string>_calendar</string>
 
     <key>GroupName</key>
-    <string>calendar</string>
+    <string>_calendar</string>
 
     <key>ProcessType</key>
     <string>Combined</string>

Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/conf/caldavd-test.plist	2015-10-05 21:18:27 UTC (rev 15165)
@@ -537,7 +537,7 @@
     <key>AccountingCategories</key>
     <dict>
       <key>HTTP</key>
-      <false/>
+      <true/>
       <key>iTIP</key>
       <false/>
       <key>iTIP-VFREEBUSY</key>
@@ -897,12 +897,16 @@
     <!-- Support for Postgres -->
     <key>Postgres</key>
     <dict>
+      <key>ListenAddresses</key>
+      <array>
+      <string>127.0.0.1</string>
+      </array>
       <key>Options</key>
       <array>
-      	<!-- Optional extra logging for posgres -->
-      	<!-- <string>-c log_lock_waits=TRUE</string> -->
-      	<!-- <string>-c log_statement=all</string> -->
-      	<!-- <string>-c log_line_prefix='%t [%p]: [%l] '</string> -->
+        <!-- Optional extra logging for posgres -->
+        <!-- <string>-c log_lock_waits=TRUE</string> -->
+        <!-- <string>-c log_statement=all</string> -->
+        <!-- <string>-c log_line_prefix='%t [%p]: [%l] '</string> -->
       </array>
     </dict>
 

Modified: CalendarServer/trunk/contrib/performance/loadtest/clients.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/clients.plist	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/clients.plist	2015-10-05 21:18:27 UTC (rev 15165)
@@ -31,16 +31,16 @@
 
 				<!-- Here is a OS X client simulator. -->
 				<key>software</key>
-				<string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+				<string>contrib.performance.loadtest.ical.OS_X_10_11</string>
 
-				<!-- Arguments to use to initialize the OS_X_10_7 instance. -->
+				<!-- Arguments to use to initialize the OS_X_10_11 instance. -->
 				<key>params</key>
 				<dict>
 					<!-- Name that appears in logs. -->
 					<key>title</key>
-					<string>10.7</string>
-	
-					<!-- OS_X_10_7 can poll the calendar home at some interval. This is
+					<string>10.11</string>
+
+					<!-- OS_X_10_11 can poll the calendar home at some interval. This is
 						in seconds. -->
 					<key>calendarHomePollInterval</key>
 					<integer>30</integer>
@@ -51,7 +51,7 @@
 						and use it if possible. Still fall back to polling if there is no xmpp push
 						advertised. -->
 					<key>supportPush</key>
-					<false />
+					<true/>
 
 					<key>supportAmpPush</key>
 					<true/>
@@ -75,7 +75,7 @@
 						<key>params</key>
 						<dict>
 							<key>enabled</key>
-							<true/>
+							<false/>
 
 							<!-- Define the interval (in seconds) at which this profile will use
 								its client to create a new event. -->
@@ -144,13 +144,13 @@
 										<!-- Half of all events will be non-recurring -->
 										<key>none</key>
 										<integer>50</integer>
-										
+
 										<!-- Daily and weekly are pretty common -->
 										<key>daily</key>
 										<integer>10</integer>
 										<key>weekly</key>
 										<integer>20</integer>
-										
+
 										<!-- Monthly, yearly, daily & weekly limit not so common -->
 										<key>monthly</key>
 										<integer>2</integer>
@@ -160,7 +160,7 @@
 										<integer>2</integer>
 										<key>weeklylimit</key>
 										<integer>5</integer>
-										
+
 										<!-- Work days pretty common -->
 										<key>workdays</key>
 										<integer>10</integer>
@@ -247,13 +247,13 @@
 										<!-- Half of all events will be non-recurring -->
 										<key>none</key>
 										<integer>50</integer>
-										
+
 										<!-- Daily and weekly are pretty common -->
 										<key>daily</key>
 										<integer>25</integer>
 										<key>weekly</key>
 										<integer>25</integer>
-										
+
 										<!-- Monthly, yearly, daily & weekly limit not so common -->
 										<key>monthly</key>
 										<integer>0</integer>
@@ -263,7 +263,7 @@
 										<integer>0</integer>
 										<key>weeklylimit</key>
 										<integer>0</integer>
-										
+
 										<!-- Work days pretty common -->
 										<key>workdays</key>
 										<integer>0</integer>
@@ -273,6 +273,99 @@
 						</dict>
 					</dict>
 
+					<!-- Picks a random event and changes the title -->
+					<dict>
+						<key>class</key>
+						<string>contrib.performance.loadtest.profiles.TitleChanger</string>
+
+						<key>params</key>
+						<dict>
+							<key>enabled</key>
+							<false/>
+
+							<!-- Define the interval (in seconds) at which this profile will use
+								its client to create a new event. -->
+							<key>interval</key>
+							<integer>10</integer>
+
+						</dict>
+					</dict>
+
+					<!-- Picks a random event and attaches -->
+					<dict>
+						<key>class</key>
+						<string>contrib.performance.loadtest.profiles.Attacher</string>
+
+						<key>params</key>
+						<dict>
+							<key>enabled</key>
+							<false/>
+
+							<!-- Define the interval (in seconds) at which this profile will use
+								its client to create a new event. -->
+							<key>interval</key>
+							<integer>10</integer>
+
+							<!-- Define the attachment size distribution. -->
+							<key>fileSizeDistribution</key>
+							<dict>
+								<key>type</key>
+								<string>contrib.performance.stats.NormalDistribution</string>
+								<key>params</key>
+								<dict>
+									<!-- mu gives the mean of the normal distribution (in seconds). -->
+									<key>mu</key>
+									<integer>500000</integer>
+
+									<!-- and sigma gives its standard deviation. -->
+									<key>sigma</key>
+									<integer>100000</integer>
+								</dict>
+							</dict>
+
+						</dict>
+					</dict>
+
+					<!-- Removes events from calendars exceeding a threshold -->
+					<dict>
+						<key>class</key>
+						<string>contrib.performance.loadtest.profiles.EventCountLimiter</string>
+
+						<key>params</key>
+						<dict>
+							<key>enabled</key>
+							<false/>
+
+							<!-- Define the interval (in seconds) at which this profile will check
+								for too-large collections. -->
+							<key>interval</key>
+							<integer>60</integer>
+
+							<!-- The upper bound. -->
+							<key>eventCountLimit</key>
+							<integer>10</integer>
+
+						</dict>
+					</dict>
+
+
+					<!-- Shares calendars -->
+					<dict>
+						<key>class</key>
+						<string>contrib.performance.loadtest.profiles.CalendarSharer</string>
+
+						<key>params</key>
+						<dict>
+							<key>enabled</key>
+							<true/>
+
+							<!-- Define the interval (in seconds) at which this profile will share calendars. -->
+							<key>interval</key>
+							<integer>60</integer>
+
+						</dict>
+					</dict>
+
 					<!-- This profile invites some number of new attendees to new events. -->
 					<dict>
 						<key>class</key>
@@ -281,7 +374,7 @@
 						<key>params</key>
 						<dict>
 							<key>enabled</key>
-							<true/>
+							<false/>
 
 							<!-- Define the frequency at which new invitations will be sent out. -->
 							<key>sendInvitationDistribution</key>
@@ -292,7 +385,7 @@
 								<dict>
 									<!-- mu gives the mean of the normal distribution (in seconds). -->
 									<key>mu</key>
-									<integer>60</integer>
+									<integer>10</integer>
 
 									<!-- and sigma gives its standard deviation. -->
 									<key>sigma</key>
@@ -301,12 +394,12 @@
 							</dict>
 
 							<!-- Define the distribution of who will be invited to an event.
-							
+
 								When inviteeClumping is turned on each invitee is based on a sample of
 								users "close to" the organizer based on account index. If the clumping
 								is too "tight" for the requested number of attendees, then invites for
 								those larger numbers will simply fail (the sim will report that situation).
-								
+
 								When inviteeClumping is off invitees will be sampled across an entire
 								range of account indexes. In this case the distribution ought to be a
 								UniformIntegerDistribution with min=0 and max set to the number of accounts.
@@ -330,13 +423,13 @@
 							<true/>
 
 							<!-- Define the distribution of how many attendees will be invited to an event.
-							
+
 								LogNormal is the best fit to observed data.
 
 
 								For LogNormal "mode" is the peak, "mean" is the mean value.	For invites,
 								mode should typically be 1, and mean whatever matches the user behavior.
-								Our typical mean is 6. 							
+								Our typical mean is 6.
 							     -->
 							<key>inviteeCountDistribution</key>
 							<dict>
@@ -418,13 +511,13 @@
 										<!-- Half of all events will be non-recurring -->
 										<key>none</key>
 										<integer>50</integer>
-										
+
 										<!-- Daily and weekly are pretty common -->
 										<key>daily</key>
 										<integer>10</integer>
 										<key>weekly</key>
 										<integer>20</integer>
-										
+
 										<!-- Monthly, yearly, daily & weekly limit not so common -->
 										<key>monthly</key>
 										<integer>2</integer>
@@ -434,7 +527,7 @@
 										<integer>2</integer>
 										<key>weeklylimit</key>
 										<integer>5</integer>
-										
+
 										<!-- Work days pretty common -->
 										<key>workdays</key>
 										<integer>10</integer>
@@ -459,7 +552,7 @@
 								accepting it.
 
 								For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-								(i.e., half of the user have accepted by that time).								
+								(i.e., half of the user have accepted by that time).
 							-->
 							<key>acceptDelayDistribution</key>
 							<dict>

Modified: CalendarServer/trunk/contrib/performance/loadtest/config.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/config.plist	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/config.plist	2015-10-05 21:18:27 UTC (rev 15165)
@@ -21,7 +21,7 @@
 	<dict>
 		<!-- Identify the server to be load tested. -->
 		<key>server</key>
-		<string>https://127.0.0.1:8443</string>
+		<string>https://localhost:8443</string>
 
 		<!-- The template URI for doing initial principal lookup on. -->
 		<key>principalPathTemplate</key>
@@ -96,7 +96,7 @@
 			<dict>
 				<!-- groups gives the total number of groups of clients to introduce. -->
 				<key>groups</key>
-				<integer>20</integer>
+				<integer>1</integer>
 
 				<!-- groupSize is the number of clients in each group of clients. It's
 					really only a "smooth" ramp up if this is pretty small. -->
@@ -118,7 +118,7 @@
 		<!-- Define some log observers to report on the load test. -->
 		<key>observers</key>
 		<array>
-			<!-- ReportStatistics generates an end-of-run summary of the HTTP requests 
+			<!-- ReportStatistics generates an end-of-run summary of the HTTP requests
 				made, their timings, and their results. -->
 			<dict>
 				<key>type</key>
@@ -128,7 +128,7 @@
 					<!-- The thresholds for each request type -->
 					<key>thresholdsPath</key>
 					<string>contrib/performance/loadtest/thresholds.json</string>
-					
+
 					<!-- The benchmarks for overall QoS -->
 					<key>benchmarksPath</key>
 					<string>contrib/performance/loadtest/benchmarks.json</string>
@@ -138,8 +138,8 @@
 					<real>1.0</real>
 				</dict>
 			</dict>
-	
-			<!-- RequestLogger generates a realtime log of all HTTP requests made 
+
+			<!-- RequestLogger generates a realtime log of all HTTP requests made
 				during the load test. -->
 			<dict>
 				<key>type</key>
@@ -148,9 +148,9 @@
 				<dict>
 				</dict>
 			</dict>
-	
-			<!-- OperationLogger generates an end-of-run summary of the gross operations 
-				performed (logical operations which may span more than one HTTP request, 
+
+			<!-- OperationLogger generates an end-of-run summary of the gross operations
+				performed (logical operations which may span more than one HTTP request,
 				such as inviting an attendee to an event). -->
 			<dict>
 				<key>type</key>
@@ -160,11 +160,11 @@
 					<!-- The thresholds for each operation type -->
 					<key>thresholdsPath</key>
 					<string>contrib/performance/loadtest/thresholds.json</string>
-					
+
 					<!-- The % of operations beyond the lag cut-off that constitute a failed test -->
 					<key>lagCutoff</key>
 					<real>1.0</real>
-					
+
 					<!-- The % of failures that constitute a failed test -->
 					<key>failCutoff</key>
 					<real>1.0</real>

Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -18,7 +18,10 @@
 
 from caldavclientlibrary.protocol.caldav.definitions import caldavxml
 from caldavclientlibrary.protocol.caldav.definitions import csxml
+from caldavclientlibrary.protocol.calendarserver.invite import AddInvitees, RemoveInvitee, InviteUser
+from caldavclientlibrary.protocol.calendarserver.notifications import InviteNotification
 from caldavclientlibrary.protocol.url import URL
+from caldavclientlibrary.protocol.utils.xmlhelpers import BetterElementTree
 from caldavclientlibrary.protocol.webdav.definitions import davxml
 from caldavclientlibrary.protocol.webdav.propfindparser import PropFindParser
 
@@ -45,22 +48,27 @@
 from twisted.python.util import FancyEqMixin
 from twisted.web.client import Agent, ContentDecoderAgent, GzipDecoder, \
     _DeprecatedToCurrentPolicyForHTTPS
-from twisted.web.http import OK, MULTI_STATUS, CREATED, NO_CONTENT, PRECONDITION_FAILED, MOVED_PERMANENTLY, \
-    FORBIDDEN, FOUND
+from twisted.web.http import (
+    OK, MULTI_STATUS, CREATED, NO_CONTENT, PRECONDITION_FAILED,
+    MOVED_PERMANENTLY, FORBIDDEN, FOUND, NOT_FOUND
+)
 from twisted.web.http_headers import Headers
 
 from twistedcaldav.ical import Component, Property
 
 from urlparse import urlparse, urlunparse, urlsplit, urljoin
 from uuid import uuid4
-from xml.etree import ElementTree
+from xml.etree.ElementTree import ElementTree, Element, SubElement, QName
 
+from StringIO import StringIO
+
 import json
 import os
 import random
 
-ElementTree.QName.__repr__ = lambda self: '<QName %r>' % (self.text,)
+QName.__repr__ = lambda self: '<QName %r>' % (self.text,)
 
+
 def loadRequestBody(clientType, label):
     return FilePath(__file__).sibling('request-data').child(clientType).child(label + '.request').getContent()
 
@@ -247,19 +255,152 @@
                 calendar.changeToken = ""
         return calendar
 
+    @staticmethod
+    def addInviteeXML(uid, summary, readwrite=True):
+        return AddInvitees(None, '/', [uid], readwrite, summary=summary).request_data.text
 
 
+    @staticmethod
+    def removeInviteeXML(uid):
+        invitee = InviteUser()
+        # Usually an InviteUser is populated through .parseFromUser, but we only care about a uid
+        invitee.user_uid = uid
+        return RemoveInvitee(None, '/', invitee).request_data.text
+
+
+# class Notification(object):
+#     def __init__(self, serializeBasePath, url, etag, body=None):
+#         self.serializeBasePath = serializeBasePath
+#         self.url = url
+#         self.etag = etag
+#         self.body = body
+
+
+#     def serializePath(self):
+#         if self.serializeBasePath:
+#             parentPath = os.path.join(self.serializeBasePath, "notifications")
+#             if not os.path.exists(parentPath):
+#                 os.makedirs(parentPath)
+#             return os.path.join(parentPath, self.url.split("/")[-1])
+#         else:
+#             return None
+
+
+#     def serialize(self):
+#         """
+#         Create a dict of the data so we can serialize as JSON.
+#         """
+
+#         result = {}
+#         for attr in ("url", "etag"):
+#             result[attr] = getattr(self, attr)
+#         return result
+
+
+#     @staticmethod
+#     def deserialize(serializeLocation, data):
+#         """
+#         Convert dict (deserialized from JSON) into an L{Event}.
+#         """
+
+#         event = Notification(serializeLocation, None, None)
+#         for attr in ("url", "etag"):
+#             setattr(event, attr, u2str(data[attr]))
+#         return event
+
+
+#     @property
+#     def body(self):
+#         """
+#         Data always read from disk - never cached in the object.
+#         """
+#         path = self.serializePath()
+#         if path and os.path.exists(path):
+#             f = open(path)
+#             content = f.read()
+#             f.close()
+#             return content
+#         else:
+#             return None
+
+
+#     @body.setter
+#     def body(self, content):
+#         """
+#         Data always written to disk - never cached on the object.
+#         """
+#         path = self.serializePath()
+#         if path:
+#             if content is None:
+#                 os.remove(path)
+#             else:
+#                 f = open(path, "w")
+#                 f.write(content)
+#                 f.close()
+
+
+#     def removed(self):
+#         """
+#         Resource no longer exists on the server - remove associated data.
+#         """
+#         path = self.serializePath()
+#         if path and os.path.exists(path):
+#             os.remove(path)
+
+
+class NotificationCollection(object):
+    def __init__(self, url, changeToken):
+        self.url = url
+        self.changeToken = changeToken
+        self.notifications = {}
+        self.name = "notification"
+
+    def serialize(self):
+        """
+        Create a dict of the data so we can serialize as JSON.
+        """
+
+        result = {}
+        for attr in ("url", "changeToken"):
+            result[attr] = getattr(self, attr)
+        result["notifications"] = sorted(self.notifications.keys())
+        return result
+
+
+    @staticmethod
+    def deserialize(data, notifications):
+        """
+        Convert dict (deserialized from JSON) into an L{Calendar}.
+        """
+
+        coll = NotificationCollection(None, None)
+        for attr in ("url", "changeToken"):
+            setattr(coll, attr, u2str(data[attr]))
+
+        for notification in data["notifications"]:
+            url = urljoin(coll.url, notification)
+            if url in notifications:
+                coll.notifications[notification] = notifications[url]
+            else:
+                # Ughh - a notification is missing - force changeToken to empty to trigger full resync
+                coll.changeToken = ""
+        return coll
+
+
+
 class BaseClient(object):
     """
     Base interface for all simulated clients.
     """
 
-    user = None         # User account details
-    _events = None      # Cache of events keyed by href
-    _calendars = None   # Cache of calendars keyed by href
-    started = False     # Whether or not startup() has been executed
-    _client_type = None # Type of this client used in logging
-    _client_id = None   # Unique id for the client itself
+    user = None                     # User account details
+    _events = None                  # Cache of events keyed by href
+    _calendars = None               # Cache of calendars keyed by href
+    _notifications = None           # Cache of notifications keyed by href
+    _notificationCollection = None  # Cache of the notification collection
+    started = False                 # Whether or not startup() has been executed
+    _client_type = None             # Type of this client used in logging
+    _client_id = None               # Unique id for the client itself
 
 
     def _setEvent(self, href, event):
@@ -281,6 +422,21 @@
         del self._calendars[calendar + '/'].events[basePath]
 
 
+    def _setNotification(self, href, notification):
+        """
+        Cache the provided notification
+        """
+        self._notifications[href] = notification
+
+
+    def _removeNotification(self, href):
+        """
+        Remove notification from local cache.
+        """
+        self._notifications[href].removed()
+        del self._notifications[href]
+
+
     def addEvent(self, href, calendar):
         """
         Called when a profile needs to add an event (no scheduling).
@@ -410,6 +566,8 @@
     _POLL_NOTIFICATION_PROPFIND = None
     _POLL_NOTIFICATION_PROPFIND_D1 = None
 
+    _NOTIFICATION_SYNC_REPORT = None
+
     _USER_LIST_PRINCIPAL_PROPERTY_SEARCH = None
     _POST_AVAILABILITY = None
 
@@ -453,6 +611,8 @@
             calendarHomePollInterval = self.CALENDAR_HOME_POLL_INTERVAL
         self.calendarHomePollInterval = calendarHomePollInterval
 
+        self.calendarHomeHref = None
+
         self.supportPush = supportPush
 
         self.supportAmpPush = supportAmpPush
@@ -730,8 +890,10 @@
             depth='1',
             method_label="PROPFIND{home}",
         )
-        calendars = self._extractCalendars(result, calendarHomeSet)
-        returnValue((calendars, result,))
+        calendars, notificationCollection = self._extractCalendars(
+            result, calendarHomeSet
+        )
+        returnValue((calendars, notificationCollection, result,))
 
 
     @inlineCallbacks
@@ -777,6 +939,10 @@
         that from the response.
         """
         calendars = []
+        notificationCollection = None
+
+        changeTag = davxml.sync_token if self.supportSync else csxml.getctag
+
         for href in results:
 
             if href == calendarHome:
@@ -810,7 +976,6 @@
                             for comp in nodes[caldavxml.supported_calendar_component_set]:
                                 componentTypes.add(comp.get("name").upper())
 
-                    changeTag = davxml.sync_token if self.supportSync else csxml.getctag
                     calendars.append(Calendar(
                         nodeType.tag,
                         componentTypes,
@@ -819,9 +984,16 @@
                         textProps.get(changeTag, None),
                     ))
                     break
-        return calendars
+                elif nodeType.tag == csxml.notification:
+                    textProps = results[href].getTextProperties()
+                    notificationCollection = NotificationCollection(
+                        href,
+                        textProps.get(changeTag, None)
+                    )
 
+        return calendars, notificationCollection
 
+
     def _updateCalendar(self, calendar, newToken):
         """
         Update the local cached data for a calendar in an appropriate manner.
@@ -1041,6 +1213,7 @@
         try:
             result = yield self._newOperation("push" if push else "poll", self._poll(calendarHomeSet, firstTime))
         finally:
+            print("*** BACK FROM NEW OPERATION, result=", result)
             if result:
                 try:
                     self._checking.remove(calendarHomeSet)
@@ -1050,12 +1223,132 @@
 
 
     @inlineCallbacks
+    def _updateNotifications(self, oldToken, newToken):
+
+        fullSync = not oldToken
+
+        # Get the list of notificatinon xml resources
+
+        result = yield self._report(
+            self._notificationCollection.url,
+            self._NOTIFICATION_SYNC_REPORT % {'sync-token': oldToken},
+            depth='1',
+            allowedStatus=(MULTI_STATUS, FORBIDDEN,),
+            otherTokens=True,
+            method_label="REPORT{sync}" if oldToken else "REPORT{sync-init}",
+        )
+        if result is None:
+            if not fullSync:
+                fullSync = True
+                result = yield self._report(
+                    self._notificationCollection.url,
+                    self._NOTIFICATION_SYNC_REPORT % {'sync-token': ''},
+                    depth='1',
+                    otherTokens=True,
+                    method_label="REPORT{sync}" if oldToken else "REPORT{sync-init}",
+                )
+            else:
+                raise IncorrectResponseCode((MULTI_STATUS,), None)
+
+        result, others = result
+
+        # Scan for the sharing invites
+        inviteNotifications = []
+        toDelete = []
+        for responseHref in result:
+            if responseHref == self._notificationCollection.url:
+                continue
+
+            # try:
+            #     etag = result[responseHref].getTextProperties()[davxml.getetag]
+            # except KeyError:
+            #     # XXX Ignore things with no etag?  Seems to be dropbox.
+            #     continue
+
+            toDelete.append(responseHref)
+
+            if result[responseHref].getStatus() / 100 == 2:
+                # Get the notification
+                response = yield self._request(
+                    OK,
+                    'GET',
+                    self.root + responseHref.encode('utf-8'),
+                    method_label="GET{notification}",
+                )
+                body = yield readBody(response)
+                node = ElementTree(file=StringIO(body)).getroot()
+                if node.tag == str(csxml.notification):
+                    nurl = URL(url=responseHref)
+                    for child in node.getchildren():
+                        if child.tag == str(csxml.invite_notification):
+                            if child.find(str(csxml.invite_noresponse)) is not None:
+                                inviteNotifications.append(
+                                    InviteNotification().parseFromNotification(
+                                        nurl, child
+                                    )
+                                )
+
+        # Accept the invites
+        for notification in inviteNotifications:
+            # Create an invite-reply
+            """
+            <?xml version="1.0" encoding="UTF-8"?>
+            <C:invite-reply xmlns:C="http://calendarserver.org/ns/">
+              <A:href xmlns:A="DAV:">urn:x-uid:10000000-0000-0000-0000-000000000002</A:href>
+              <C:invite-accepted/>
+              <C:hosturl>
+                <A:href xmlns:A="DAV:">/calendars/__uids__/10000000-0000-0000-0000-000000000001/A1DDC58B-651E-4B1C-872A-C6588CA09ADB</A:href>
+              </C:hosturl>
+              <C:in-reply-to>d2683fa9-7a50-4390-82bb-cbcea5e0fa86</C:in-reply-to>
+              <C:summary>to share</C:summary>
+            </C:invite-reply>
+            """
+            reply = Element(csxml.invite_reply)
+            href = SubElement(reply, davxml.href)
+            href.text = notification.user_uid
+            SubElement(reply, csxml.invite_accepted)
+            hosturl = SubElement(reply, csxml.hosturl)
+            href = SubElement(hosturl, davxml.href)
+            href.text = notification.hosturl
+            inReplyTo = SubElement(reply, csxml.in_reply_to)
+            inReplyTo.text = notification.uid
+            summary = SubElement(reply, csxml.summary)
+            summary.text = notification.summary
+
+            xmldoc = BetterElementTree(reply)
+            os = StringIO()
+            xmldoc.writeUTF8(os)
+            # Post to my calendar home
+            response = yield self.postXML(
+                self.calendarHomeHref,
+                os.getvalue(),
+                "POST{invite-accept}"
+            )
+
+        # Delete all the notification resources
+        for responseHref in toDelete:
+            response = yield self._request(
+                (NO_CONTENT, NOT_FOUND),
+                'DELETE',
+                self.root + responseHref.encode('utf-8'),
+                method_label="DELETE{invite}",
+            )
+
+        self._notificationCollection.changeToken = newToken
+
+
+
+    @inlineCallbacks
     def _poll(self, calendarHomeSet, firstTime):
+        """
+        This gets called during a normal poll or in response to a push
+        """
+
         if calendarHomeSet in self._checking:
             returnValue(False)
         self._checking.add(calendarHomeSet)
 
-        calendars, results = yield self._calendarHomePropfind(calendarHomeSet)
+        calendars, notificationCollection, results = yield self._calendarHomePropfind(calendarHomeSet)
 
         # First time operations
         if firstTime:
@@ -1073,12 +1366,24 @@
                 # Calendar changed - reload it
                 yield self._updateCalendar(self._calendars[cal.url], newToken)
 
-        # When there is no sync REPORT, clients have to do a full PROPFIND
-        # on the notification collection because there is no ctag
-        if self.notificationURL is not None and not self.supportSync:
-            yield self._notificationPropfind(self.notificationURL)
-            yield self._notificationChangesPropfind(self.notificationURL)
+        if notificationCollection is not None:
+            if self._notificationCollection:
+                oldToken = self._notificationCollection.changeToken
+            else:
+                oldToken = ""
+            self._notificationCollection = notificationCollection
+            newToken = notificationCollection.changeToken
+            yield self._updateNotifications(oldToken, newToken)
 
+        # FIXME: isn't sync report the new norm, and therefore we can remove
+        # the following?
+
+        # # When there is no sync REPORT, clients have to do a full PROPFIND
+        # # on the notification collection because there is no ctag
+        # if self.notificationURL is not None and not self.supportSync:
+        #     yield self._notificationPropfind(self.notificationURL)
+        #     yield self._notificationChangesPropfind(self.notificationURL)
+
         # One time delegate expansion
         if firstTime:
             yield self._pollFirstTime2()
@@ -1265,6 +1570,8 @@
             calendarHome = hrefs[caldavxml.calendar_home_set].toString()
             if calendarHome is None:
                 raise MissingCalendarHome
+            else:
+                self.calendarHomeHref = calendarHome
             yield self._checkCalendarsForEvents(calendarHome, firstTime=True)
             returnValue(calendarHome)
         calendarHome = yield self._newOperation("startup: %s" % (self.title,), startup())
@@ -1329,6 +1636,7 @@
             "principalURL": self.principalURL,
             "calendars": [calendar.serialize() for calendar in sorted(self._calendars.values(), key=lambda x:x.name)],
             "events": [event.serialize() for event in sorted(self._events.values(), key=lambda x:x.url)],
+            "notificationCollection" : self._notificationCollection.serialize(),
         }
 
         # Write JSON data
@@ -1452,18 +1760,11 @@
             elif attendee.hasParameter('EMAIL'):
                 email = attendee.parameterValue('EMAIL').encode("utf-8")
 
-            # First try to discover some names to supply to the
-            # auto-completion
+            search = "<C:search-token>{}</C:search-token>".format(prefix)
+            body = self._CALENDARSERVER_PRINCIPAL_SEARCH_REPORT.format(
+                context="attendee", searchTokens=search)
             yield self._report(
-                self.principalCollection,
-                self._USER_LIST_PRINCIPAL_PROPERTY_SEARCH % {
-                    'displayname': prefix,
-                    'email': prefix,
-                    'firstname': prefix,
-                    'lastname': prefix,
-                },
-                depth=None,
-                method_label="REPORT{psearch}",
+                '/principals/', body, depth=None, method_label="REPORT{cpsearch}"
             )
 
             # Now learn about the attendee's availability
@@ -1475,6 +1776,7 @@
             )
 
 
+
     @inlineCallbacks
     def changeEventAttendee(self, href, oldAttendee, newAttendee):
         event = self._events[href]
@@ -1522,7 +1824,7 @@
         self._removeEvent(href)
 
         response = yield self._request(
-            NO_CONTENT,
+            (NO_CONTENT, NOT_FOUND),
             'DELETE',
             self.root + href.encode('utf-8'),
             method_label="DELETE{event}",
@@ -1702,7 +2004,42 @@
         returnValue(body)
 
 
+    @inlineCallbacks
+    def postAttachment(self, href, content):
+        url = self.root + "{0}?{1}".format(href, "action=attachment-add")
+        filename = 'file-{}.txt'.format(len(content))
+        headers = Headers({
+            'Content-Disposition': ['attachment; filename="{}"'.format(filename)]
+        })
+        response = yield self._request(
+            CREATED,
+            'POST',
+            url,
+            headers=headers,
+            body=StringProducer(content),
+            method_label="POST{attach}"
+        )
+        body = yield readBody(response)
+        returnValue(body)
 
+
+    @inlineCallbacks
+    def postXML(self, href, content, label):
+        headers = Headers({
+            'content-type': ['text/xml']
+        })
+        response = yield self._request(
+            (OK, CREATED, MULTI_STATUS),
+            'POST',
+            self.root + href,
+            headers=headers,
+            body=StringProducer(content),
+            method_label=label
+        )
+        body = yield readBody(response)
+        returnValue(body)
+
+
 class OS_X_10_6(BaseAppleClient):
     """
     Implementation of the OS X 10.6 iCal network behavior.
@@ -1868,7 +2205,99 @@
         returnValue(principal)
 
 
+class OS_X_10_11(BaseAppleClient):
+    """
+    Implementation of the OS X 10.11 Calendar.app network behavior.
+    """
 
+    _client_type = "OS X 10.11"
+
+    USER_AGENT = "Mac+OS+X/10.11 (15A283) CalendarAgent/361"
+
+    # The default interval, used if none is specified in external
+    # configuration.  This is also the actual value used by El
+    # Capital Calendar.app.
+    CALENDAR_HOME_POLL_INTERVAL = 15 * 60  # in seconds
+
+    # The maximum number of resources to retrieve in a single multiget
+    MULTIGET_BATCH_SIZE = 50
+
+    # Override and turn on if client supports Sync REPORT
+    _SYNC_REPORT = True
+
+    # Override and turn off if client does not support attendee lookups
+    _ATTENDEE_LOOKUPS = True
+
+    # Request body data
+    _LOAD_PATH = "OS_X_10_11"
+
+    _STARTUP_WELL_KNOWN = loadRequestBody(_LOAD_PATH, 'startup_well_known_propfind')
+    _STARTUP_PRINCIPAL_PROPFIND_INITIAL = loadRequestBody(_LOAD_PATH, 'startup_principal_initial_propfind')
+    _STARTUP_PRINCIPAL_PROPFIND = loadRequestBody(_LOAD_PATH, 'startup_principal_propfind')
+    _STARTUP_PRINCIPALS_REPORT = loadRequestBody(_LOAD_PATH, 'startup_principals_report')
+    _STARTUP_PRINCIPAL_EXPAND = loadRequestBody(_LOAD_PATH, 'startup_principal_expand')
+
+    _STARTUP_CREATE_CALENDAR = loadRequestBody(_LOAD_PATH, 'startup_create_calendar')
+    _STARTUP_PROPPATCH_CALENDAR_COLOR = loadRequestBody(_LOAD_PATH, 'startup_calendar_color_proppatch')
+    # _STARTUP_PROPPATCH_CALENDAR_NAME = loadRequestBody(_LOAD_PATH, 'startup_calendar_displayname_proppatch')
+    _STARTUP_PROPPATCH_CALENDAR_ORDER = loadRequestBody(_LOAD_PATH, 'startup_calendar_order_proppatch')
+    _STARTUP_PROPPATCH_CALENDAR_TIMEZONE = loadRequestBody(_LOAD_PATH, 'startup_calendar_timezone_proppatch')
+
+    _POLL_CALENDARHOME_PROPFIND = loadRequestBody(_LOAD_PATH, 'poll_calendarhome_depth1_propfind')
+    _POLL_CALENDAR_PROPFIND = loadRequestBody(_LOAD_PATH, 'poll_calendar_propfind')
+    _POLL_CALENDAR_PROPFIND_D1 = loadRequestBody(_LOAD_PATH, 'poll_calendar_depth1_propfind')
+    _POLL_CALENDAR_MULTIGET_REPORT = loadRequestBody('OS_X_10_7', 'poll_calendar_multiget')
+    _POLL_CALENDAR_MULTIGET_REPORT_HREF = loadRequestBody('OS_X_10_7', 'poll_calendar_multiget_hrefs')
+    _POLL_CALENDAR_SYNC_REPORT = loadRequestBody('OS_X_10_7', 'poll_calendar_sync')
+    _POLL_NOTIFICATION_PROPFIND = loadRequestBody(_LOAD_PATH, 'poll_calendar_propfind')
+    _POLL_NOTIFICATION_PROPFIND_D1 = loadRequestBody(_LOAD_PATH, 'poll_notification_depth1_propfind')
+
+    _NOTIFICATION_SYNC_REPORT = loadRequestBody(_LOAD_PATH, 'notification_sync')
+
+    _USER_LIST_PRINCIPAL_PROPERTY_SEARCH = loadRequestBody('OS_X_10_7', 'user_list_principal_property_search')
+    _POST_AVAILABILITY = loadRequestBody('OS_X_10_7', 'post_availability')
+
+    _CALENDARSERVER_PRINCIPAL_SEARCH_REPORT = loadRequestBody(_LOAD_PATH, 'principal_search_report')
+
+
+    def _addDefaultHeaders(self, headers):
+        """
+        Add the clients default set of headers to ones being used in a request.
+        Default is to add User-Agent, sub-classes should override to add other
+        client specific things, Accept etc.
+        """
+
+        super(OS_X_10_11, self)._addDefaultHeaders(headers)
+        headers.setRawHeaders('Accept', ['*/*'])
+        headers.setRawHeaders('Accept-Language', ['en-us'])
+        headers.setRawHeaders('Accept-Encoding', ['gzip,deflate'])
+        headers.setRawHeaders('Connection', ['keep-alive'])
+
+
+    @inlineCallbacks
+    def startup(self):
+        # Try to read data from disk - if it succeeds self.principalURL will be set
+        self.deserialize()
+
+        if self.principalURL is None:
+            # PROPFIND well-known with redirect
+            response = yield self._startupPropfindWellKnown()
+            hrefs = response.getHrefProperties()
+            if davxml.current_user_principal in hrefs:
+                self.principalURL = hrefs[davxml.current_user_principal].toString()
+            elif davxml.principal_URL in hrefs:
+                self.principalURL = hrefs[davxml.principal_URL].toString()
+            else:
+                # PROPFIND principal path to retrieve actual principal-URL
+                response = yield self._principalPropfindInitial(self.record.uid)
+                hrefs = response.getHrefProperties()
+                self.principalURL = hrefs[davxml.principal_URL].toString()
+
+        # Using the actual principal URL, retrieve principal information
+        principal = yield self._extractPrincipalDetails()
+        returnValue(principal)
+
+
 class iOS_5(BaseAppleClient):
     """
     Implementation of the iOS 5 network behavior.

Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/population.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/population.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -162,10 +162,11 @@
 
 
 class CalendarClientSimulator(object):
-    def __init__(self, records, populator, parameters, reactor, server,
+    def __init__(self, records, populator, random, parameters, reactor, server,
                  principalPathTemplate, serializationPath, workerIndex=0, workerCount=1):
         self._records = records
         self.populator = populator
+        self._random = random
         self.reactor = reactor
         self.server = server
         self.principalPathTemplate = principalPathTemplate
@@ -184,6 +185,31 @@
         return self._records[index]
 
 
+    def getRandomUserRecord(self, besides=None):
+        count = len(self._records)
+
+        if count == 0:
+            # No records!
+            return None
+
+        if count == 1 and besides == 0:
+            # There is only one item and caller doesn't want it!
+            return None
+
+        for i in xrange(100):
+            # Try to find one that is not "besides"
+            n = self._random.randint(0, count - 1)
+            if besides != n:
+                # Got it.
+                break
+        else:
+            # Give up
+            return None
+        print("SELECTION, besides=", besides, "count=", count, "n=", n)
+        return self._records[n]
+
+
+
     def _nextUserNumber(self):
         result = self._user
         self._user += 1
@@ -638,7 +664,7 @@
     parameters.addClient(
         1, ClientType(OS_X_10_6, [Eventer, Inviter, Accepter]))
     simulator = CalendarClientSimulator(
-        populator, parameters, reactor, '127.0.0.1', 8008)
+        populator, r, parameters, reactor, '127.0.0.1', 8008)
 
     arrivalPolicy = SmoothRampUp(groups=10, groupSize=1, interval=3)
     arrivalPolicy.run(reactor, simulator)

Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/profiles.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/profiles.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -31,7 +31,7 @@
 from twisted.python import context
 from twisted.python.log import msg
 from twisted.python.failure import Failure
-from twisted.internet.defer import Deferred, succeed, fail
+from twisted.internet.defer import Deferred, succeed, fail, inlineCallbacks, returnValue
 from twisted.internet.task import LoopingCall
 from twisted.web.http import PRECONDITION_FAILED
 
@@ -40,7 +40,7 @@
 from contrib.performance.stats import NearFutureDistribution, NormalDistribution, UniformDiscreteDistribution, mean, median
 from contrib.performance.stats import LogNormalDistribution, RecurrenceDistribution
 from contrib.performance.loadtest.logger import SummarizingMixin
-from contrib.performance.loadtest.ical import IncorrectResponseCode
+from contrib.performance.loadtest.ical import Calendar, IncorrectResponseCode
 
 from pycalendar.datetime import DateTime
 from pycalendar.duration import Duration
@@ -78,9 +78,46 @@
             cal
             for cal
             in self._client._calendars.itervalues()
-            if cal.resourceType == calendarType and componentType in cal.componentTypes]
+            if cal.resourceType == calendarType and componentType in cal.componentTypes
+        ]
 
 
+    def _getRandomCalendarOfType(self, componentType):
+        """
+        Return a random L{Calendar} object from the current user
+        or C{None} if there are no calendars to work with
+        """
+        calendars = self._calendarsOfType(caldavxml.calendar, componentType)
+        if not calendars:
+            return None
+        # Choose a random calendar
+        calendar = self.random.choice(calendars)
+        return calendar
+
+
+    def _getRandomEventOfType(self, componentType):
+        """
+        Return a random L{Event} object from the current user
+        or C{None} if there are no events to work with
+        """
+        calendars = self._calendarsOfType(caldavxml.calendar, componentType)
+        while calendars:
+            calendar = self.random.choice(calendars)
+            calendars.remove(calendar)
+            if not calendar.events:
+                continue
+
+            events = calendar.events.keys()
+            while events:
+                href = self.random.choice(events)
+                events.remove(href)
+                event = calendar.events[href]
+                if not event.component:
+                    continue
+                return event
+        return None
+
+
     def _isSelfAttendee(self, attendee):
         """
         Try to match one of the attendee's identifiers against one of
@@ -645,38 +682,191 @@
 
 
     def _addEvent(self):
+        # Don't perform any operations until the client is up and running
         if not self._client.started:
             return succeed(None)
 
-        calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT")
+        calendar = self._getRandomCalendarOfType('VEVENT')
 
-        while calendars:
-            calendar = self.random.choice(calendars)
-            calendars.remove(calendar)
+        # Copy the template event and fill in some of its fields
+        # to make a new event to create on the calendar.
+        vcalendar = self._eventTemplate.duplicate()
+        vevent = vcalendar.mainComponent()
+        uid = str(uuid4())
+        dtstart = self._eventStartDistribution.sample()
+        dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
+        vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
+        vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
+        vevent.replaceProperty(Property("DTSTART", dtstart))
+        vevent.replaceProperty(Property("DTEND", dtend))
+        vevent.replaceProperty(Property("UID", uid))
 
-            # Copy the template event and fill in some of its fields
-            # to make a new event to create on the calendar.
-            vcalendar = self._eventTemplate.duplicate()
-            vevent = vcalendar.mainComponent()
-            uid = str(uuid4())
-            dtstart = self._eventStartDistribution.sample()
-            dtend = dtstart + Duration(seconds=self._eventDurationDistribution.sample())
-            vevent.replaceProperty(Property("CREATED", DateTime.getNowUTC()))
-            vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
-            vevent.replaceProperty(Property("DTSTART", dtstart))
-            vevent.replaceProperty(Property("DTEND", dtend))
-            vevent.replaceProperty(Property("UID", uid))
+        rrule = self._recurrenceDistribution.sample()
+        if rrule is not None:
+            vevent.addProperty(Property(None, None, None, pycalendar=rrule))
 
-            rrule = self._recurrenceDistribution.sample()
-            if rrule is not None:
-                vevent.addProperty(Property(None, None, None, pycalendar=rrule))
+        href = '%s%s.ics' % (calendar.url, uid)
+        d = self._client.addEvent(href, vcalendar)
+        return self._newOperation("create", d)
 
-            href = '%s%s.ics' % (calendar.url, uid)
-            d = self._client.addEvent(href, vcalendar)
-            return self._newOperation("create", d)
 
+class EventUpdaterBase(ProfileBase):
 
+    @inlineCallbacks
+    def action(self):
+        # Don't perform any operations until the client is up and running
+        if not self._client.started:
+            returnValue(None)
 
+        event = self._getRandomEventOfType('VEVENT')
+        if not event:
+            returnValue(None)
+        component = event.component
+        vevent = component.mainComponent()
+
+        label = yield self.modifyEvent(event.url, vevent)
+        vevent.replaceProperty(Property("DTSTAMP", DateTime.getNowUTC()))
+
+        event.component = component
+        yield self._newOperation(
+            label,
+            self._client.changeEvent(event.url)
+        )
+
+
+    def run(self):
+        self._call = LoopingCall(self.action)
+        self._call.clock = self._reactor
+        return self._call.start(self._interval)
+
+
+    def modifyEvent(self, href, vevent):
+        """Overriden by subclasses"""
+        pass
+
+
+class TitleChanger(EventUpdaterBase):
+
+    def setParameters(
+        self,
+        enabled=True,
+        interval=60,
+        titleLengthDistribution=NormalDistribution(10, 2)
+    ):
+        self.enabled = enabled
+        self._interval = interval
+        self._titleLength = titleLengthDistribution
+
+    def modifyEvent(self, _ignore_href, vevent):
+        length = max(5, int(self._titleLength.sample()))
+        vevent.replaceProperty(Property("SUMMARY", "Event" + "." * (length - 5)))
+        return succeed("update{title}")
+
+
+class Attacher(EventUpdaterBase):
+
+    def setParameters(
+        self,
+        enabled=True,
+        interval=60,
+        fileSizeDistribution=NormalDistribution(1024, 1),
+    ):
+        self.enabled = enabled
+        self._interval = interval
+        self._fileSize = fileSizeDistribution
+
+    @inlineCallbacks
+    def modifyEvent(self, href, vevent):
+        fileSize = int(self._fileSize.sample())
+        yield self._client.postAttachment(href, 'x' * fileSize)
+        returnValue("attach{files}")
+
+
+class EventCountLimiter(EventUpdaterBase):
+    """
+    Examines the number of events in each calendar collection, and when that
+    count exceeds eventCountLimit, events are randomly removed until the count
+    falls back to the limit.
+    """
+
+    def setParameters(
+        self,
+        enabled=True,
+        interval=60,
+        eventCountLimit=1000
+    ):
+        self.enabled = enabled
+        self._interval = interval
+        self._limit = eventCountLimit
+
+    @inlineCallbacks
+    def action(self):
+        # Don't perform any operations until the client is up and running
+        if not self._client.started:
+            returnValue(None)
+
+        for calendar in self._calendarsOfType(caldavxml.calendar, "VEVENT"):
+            while len(calendar.events) > self._limit:
+                event = calendar.events[self.random.choice(calendar.events.keys())]
+                yield self._client.deleteEvent(event.url)
+
+
+
+class CalendarSharer(ProfileBase):
+    """
+    A Calendar user who shares and un-shares other users to calendars.
+    """
+    def setParameters(
+        self,
+        enabled=True,
+        interval=60
+    ):
+        self.enabled = enabled
+        self._interval = interval
+
+
+    def run(self):
+        self._call = LoopingCall(self.action)
+        self._call.clock = self._reactor
+        return self._call.start(self._interval)
+
+
+    @inlineCallbacks
+    def action(self):
+        # Don't perform any operations until the client is up and running
+        if not self._client.started:
+            returnValue(None)
+
+        yield self.shareCalendar()
+
+
+    @inlineCallbacks
+    def shareCalendar(self):
+
+        # pick a calendar
+        calendar = self._getRandomCalendarOfType('VEVENT')
+        if not calendar:
+            returnValue(None)
+
+        # pick a random sharee
+        shareeRecord = self._sim.getRandomUserRecord(besides=self._number)
+        if shareeRecord is None:
+            returnValue(None)
+
+        # POST the sharing invite
+        mailto = "mailto:{}".format(shareeRecord.email)
+        body = Calendar.addInviteeXML(mailto, calendar.name, readwrite=True)
+        yield self._client.postXML(
+            calendar.url,
+            body,
+            label="POST{share-calendar}"
+        )
+
+
+
+# Is the purpose of this profile "EventUpdater" simply to keep updating the same
+# resource over and over?
+
 class EventUpdater(ProfileBase):
     """
     A Calendar user who creates a new event, and then updates its alarm.
@@ -741,6 +931,7 @@
 
 
     def _initEvent(self):
+        # Don't perform any operations until the client is up and running
         if not self._client.started:
             return succeed(None)
 
@@ -840,6 +1031,7 @@
 
 
     def _addTask(self):
+        # Don't perform any operations until the client is up and running
         if not self._client.started:
             return succeed(None)
 

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/Profile
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/Profile	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/Profile	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,267 @@
+PROPFIND ./well-known/caldav				->	/principals/
+	current-user-principal
+	principal-URL
+	resourcetype
+
+PROPFIND /principals/						->
+	current-user-principal						/principals/__uids__/<uid>
+	principal-URL								----
+	resourcetype								collection
+
+OPTIONS /principals/__uids__/<uid>/
+
+PROPFIND /principals/__uids__/<uid>/
+	calendar-home-set							/calendars/__uids__/<uid>/
+	calendar-user-address-set					mailto:user#@example.com
+												urn:uuid:<uid>
+												urn:x-uid:<uid>
+	current-user-principal						/principals/__uids__/<uid>/
+	displayname									User #
+	dropbox-home-URL							/calendars/__uids__/<uid>/dropbox/
+	email-address-set							user#@example.com
+	notification-URL							/calendars/__uids__/<uid>/notification/
+	principal-collection-set					/principals/
+	principal-URL								/principals/__uids__/<uid>/
+	resource-id									urn:x-uid:<uid>
+	schedule-inbox-URL							/calendars/__uids__/<uid>/inbox/
+	schedule-outbox-URL							/calendars/__uids__/<uid>/outbox/
+	supported-report-set						acl-principal-prop-set
+												principal-match
+												principal-property-search
+												expand-property
+												calendarserver-principal-search
+
+OPTIONS /principals/__uids__/<uid>
+
+REPORT /principals/							-> 
+	principal-search-property-set				displayname
+												email-address-set
+												calendar-user-address-set
+												calendar-user-type 
+
+PROPFIND /calendars/__uids__/<uid>/inbox/	->	
+	calendar-availability						???
+
+PROPFIND /calendars/__uids__/<uid>/
+Depth 1
+	add-member									
+	allowed-sharing-modes									
+	autoprovisioned									
+	bulk-requests									
+	calendar-alarm									
+	calendar-color									
+	calendar-description									
+	calendar-free-busy-set									
+	calendar-order									
+	calendar-timezone									
+	current-user-privilege-set					all/read/read-free-busy/write/write-properties/write-content/bind/unbind/unlock/read-acl/write-acl/read-current-user-privilege-set				
+	default-alarm-vevent-date									
+	default-alarm-vevent-datetime									
+	displayname									User #
+	getctag									
+	invite									
+	language-code									
+	location-code									
+	owner										/principals/__uids__/<uid>/
+	pre-publish-url									
+	publish-url									
+	push-transports									
+	pushkey										/CalDAV/localhost/<uid>/
+	quota-available-bytes						104857600
+	quota-used-bytes							0
+	refreshrate									
+	resource-id									
+	resourcetype								collection	
+	schedule-calendar-transp									
+	schedule-default-calendar-URL									
+	source									
+	subscribed-strip-alarms									
+	subscribed-strip-attachments									
+	subscribed-strip-todos									
+	supported-calendar-component-set			VEVENT/VTODO						
+	supported-calendar-component-sets									
+	supported-report-set						acl-principal-prop-set/principal-match/principal-property-search/expand-property/calendarserver-principal-search/calendar-query/calendar-multiget/free-busy-query/addressbook-query/addressbook-multiget/sync-collection			
+	sync-token									data:,36_58/<hex>
+ 	** and more **
+
+PROPPATCH /calendars/__uids__/<uid>/		->		default-alarm-vevent-date
+PROPPATCH /calendars/__uids__/<uid>/		->		default-alarm-vevent-datetime
+
+PROPPATCH /calendars/__uids__/<uid>/calendar/	->		calendar-order
+PROPPATCH /calendars/__uids__/<uid>/calendar/	->		displayname
+PROPPATCH /calendars/__uids__/<uid>/calendar/	->		calendar-color
+PROPPATCH /calendars/__uids__/<uid>/calendar/	->		calendar-order
+PROPPATCH /calendars/__uids__/<uid>/calendar/	->		calendar-timezone
+
+PROPPATCH /calendars/__uids__/<uid>/tasks/	->		calendar-order
+PROPPATCH /calendars/__uids__/<uid>/tasks/	->		displayname
+PROPPATCH /calendars/__uids__/<uid>/tasks/	->		calendar-color
+PROPPATCH /calendars/__uids__/<uid>/tasks/	->		calendar-order
+PROPPATCH /calendars/__uids__/<uid>/tasks/	->		calendar-timezone
+
+PROPFIND /calendars/__uids__/<uid>/calendar/->
+	getctag										37_63
+	sync-token									data:,37_63/<hex>
+
+REPORT /calendars/__uids__/<uid>/calendar/ 	->
+	getcontenttype
+	getetag
+REPORT /calendar/__uids__/<uid>/calendar/
+	getcontenttype
+	getetag
+
+PROPFIND /calendars/__uids__/<uid>/			->
+	checksum-versions							???
+
+PROPFIND /calendars/__uids__/<uid>/calendar/	->
+	getctag										
+	sync-token										
+PROPFIND /calendars/__uids__/<uid>/calendar/
+	getcontenttype								httpd/unix-directory
+	getetag										"<hex>"
+
+PROPFIND /calendars/__uids__/<uid>/			-> (again?) 
+	checksum-versions
+
+PROPFIND /calendars/__uids__/<uid>/tasks/	->
+	getctag
+	sync-token
+PROPFIND /calendars/__uids__/<uid>/tasks/	->
+	getcontenttype
+	getetag
+
+PROPFIND /calendars/__uids__/<uid>/inbox/	->
+	getctag
+	sync-token
+PROPFIND /calendars/__uids__/<uid>/inbox/	->
+	getcontenttype
+	getetag
+
+PROPFIND /calendars/__uids__/<uid>/tasks/	->
+	getctag
+	sync-token
+PROPFIND /calendars/__uids__/<uid>/tasks/	->
+	getcontenttype
+	getetag
+
+PROPFIND /calendars/__uids__/<uid>/notification/	->
+	getctag
+	sync-token
+PROPFIND /calendars/__uids__/<uid>/notification/	->
+	notificationtype
+	getetag
+
+REPORT /principals/__uids__/<uid>/
+	calendar-proxy-write-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+	calendar-proxy-read-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+
+REPORT /calendars/__uids__/<uid>/
+	sync-collection
+		sync-token
+		sync-level
+		*lots of properties*
+
+PROPFIND /calendars/__uids__/<uid>/inbox/
+	getctag
+	sync-token
+
+PROPFIND /principals/__uids__/<uid>/
+	calendar-proxy-write-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+	calendar-proxy-read-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+
+----------------------------------------------------------------
+Deep Refresh (CMD + SHIFT + R)
+
+PROPFIND /principals/__uids__/<uid>/
+	<B:calendar-home-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:calendar-user-address-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:current-user-principal/>
+    <A:displayname/>
+    <C:dropbox-home-URL xmlns:C="http://calendarserver.org/ns/"/>
+    <C:email-address-set xmlns:C="http://calendarserver.org/ns/"/>
+    <C:notification-URL xmlns:C="http://calendarserver.org/ns/"/>
+    <A:principal-collection-set/>
+    <A:principal-URL/>
+    <A:resource-id/>
+    <B:schedule-inbox-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:schedule-outbox-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:supported-report-set/>
+
+OPTIONS /principals/__uids__/10000000-0000-0000-0000-000000000001/
+
+REPORT /principals/
+	principal-search-property-set
+
+PROPFIND /calendars/__uids__/10000000-0000-0000-0000-000000000001/inbox/
+	calendar-availability
+
+PROPFIND /calendars/__uids__/10000000-0000-0000-0000-000000000001/
+Depth 1
+	<A:add-member/>
+    <C:allowed-sharing-modes xmlns:C="http://calendarserver.org/ns/"/>
+    <D:autoprovisioned xmlns:D="http://apple.com/ns/ical/"/>
+    <E:bulk-requests xmlns:E="http://me.com/_namespace/"/>
+    <B:calendar-alarm xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <D:calendar-color xmlns:D="http://apple.com/ns/ical/"/>
+    <B:calendar-description xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:calendar-free-busy-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <D:calendar-order xmlns:D="http://apple.com/ns/ical/"/>
+    <B:calendar-timezone xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:current-user-privilege-set/>
+    <B:default-alarm-vevent-date xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:default-alarm-vevent-datetime xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:displayname/>
+    <C:getctag xmlns:C="http://calendarserver.org/ns/"/>
+    <C:invite xmlns:C="http://calendarserver.org/ns/"/>
+    <D:language-code xmlns:D="http://apple.com/ns/ical/"/>
+    <D:location-code xmlns:D="http://apple.com/ns/ical/"/>
+    <A:owner/>
+    <C:pre-publish-url xmlns:C="http://calendarserver.org/ns/"/>
+    <C:publish-url xmlns:C="http://calendarserver.org/ns/"/>
+    <C:push-transports xmlns:C="http://calendarserver.org/ns/"/>
+    <C:pushkey xmlns:C="http://calendarserver.org/ns/"/>
+    <A:quota-available-bytes/>
+    <A:quota-used-bytes/>
+    <D:refreshrate xmlns:D="http://apple.com/ns/ical/"/>
+    <A:resource-id/>
+    <A:resourcetype/>
+    <B:schedule-calendar-transp xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:schedule-default-calendar-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <C:source xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-alarms xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-attachments xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-todos xmlns:C="http://calendarserver.org/ns/"/>
+    <B:supported-calendar-component-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:supported-calendar-component-sets xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:supported-report-set/>
+    <A:sync-token/>
+
+PROPFIND on calendar/tasks/inbox/notifications as before
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+									
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/StartupProfile
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/StartupProfile	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/StartupProfile	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,71 @@
+PROPFIND  ./well-known/caldav					- startup_well_known_propfind
+
+PROPFIND  /principals/							- startup_principal_initial_propfind
+
+PROPFIND  /principals/__uids__/<uid>/			- startup_principal_propfind
+
+REPORT    /principals/							- startup_principals_report
+
+PROPFIND  /calendars/__uids__/<uid>/inbox/		- ???
+	calendar-availability						
+
+PROPFIND  /calendars/__uids__/<uid>/			- poll_calendar_home_depth1_propfind
+
+PROPPATCH /calendars/__uids__/<uid>/			- startup_calendarhome_default_alarm_date_proppatch
+PROPPATCH /calendars/__uids__/<uid>/			- startup_calendarhome_default_alarm_datetime_proppatch
+
+PROPPATCH /calendars/__uids__/<uid>/calendar/	- startup_calendar_order_proppatch
+PROPPATCH /calendars/__uids__/<uid>/calendar/	- startup_calendar_displayname_proppatch
+PROPPATCH /calendars/__uids__/<uid>/calendar/	- startup_calendar_color_proppatch
+PROPPATCH /calendars/__uids__/<uid>/calendar/	- startup_calendar_timezone_proppatch
+
+PROPPATCH /calendars/__uids__/<uid>/tasks/		- startup_calendar_order_proppatch
+PROPPATCH /calendars/__uids__/<uid>/tasks/		- startup_calendar_displayname_proppatch
+PROPPATCH /calendars/__uids__/<uid>/tasks/		- startup_calendar_color_proppatch
+PROPPATCH /calendars/__uids__/<uid>/tasks/		- startup_calendar_timezone_proppatch
+
+PROPFIND  /calendars/__uids__/<uid>/calendar/	- poll_calendar_propfind
+
+REPORT   /calendars/__uids__/<uid>/calendar/ 	- startup_query_events_depth1_report.request
+
+PROPFIND  /calendars/__uids__/<uid>/calendar/	- poll_calendar_propfind
+PROPFIND  /calendars/__uids__/<uid>/calendar/	- poll_calendar_depth1_propfind
+
+PROPFIND  /calendars/__uids__/<uid>/tasks/		- poll_calendar_propfind
+PROPFIND  /calendars/__uids__/<uid>/tasks/		- poll_calendar_depth1_propfind
+PROPFIND  /calendars/__uids__/<uid>/inbox/		- poll_calendar_propfind
+PROPFIND  /calendars/__uids__/<uid>/inbox/		- poll_calendar_depth1_propfind
+PROPFIND  /calendars/__uids__/<uid>/tasks/		- poll_calendar_propfind
+PROPFIND  /calendars/__uids__/<uid>/tasks/		- poll_calendar_depth1_propfind
+PROPFIND  /calendars/__uids__/<uid>/notification/	- poll_calendar_propfind
+PROPFIND  /calendars/__uids__/<uid>/notification/	- poll_notification_depth1_propfind
+
+REPORT    /principals/__uids__/<uid>/
+	calendar-proxy-write-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+	calendar-proxy-read-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+
+REPORT    /calendars/__uids__/<uid>/
+	sync-collection
+		sync-token
+		sync-level
+		*lots of properties*
+
+PROPFIND  /calendars/__uids__/<uid>/inbox/
+	getctag
+	sync-token
+
+PROPFIND  /principals/__uids__/<uid>/
+	calendar-proxy-write-for
+		calendar-user-address-set
+		email-address-set
+		displayname
+	calendar-proxy-read-for
+		calendar-user-address-set
+		email-address-set
+		displayname

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/notification_multiget_report_hrefs.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/notification_multiget_report_hrefs.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/notification_multiget_report_hrefs.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1 @@
+  <A:href xmlns:A="DAV:">%(href)s</A:href>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_depth1_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_depth1_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_depth1_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <A:getcontenttype/>
+    <A:getetag/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendar_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <C:getctag xmlns:C="http://calendarserver.org/ns/"/>
+    <A:sync-token/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <A:add-member/>
+    <C:allowed-sharing-modes xmlns:C="http://calendarserver.org/ns/"/>
+    <D:autoprovisioned xmlns:D="http://apple.com/ns/ical/"/>
+    <E:bulk-requests xmlns:E="http://me.com/_namespace/"/>
+    <B:calendar-alarm xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <D:calendar-color xmlns:D="http://apple.com/ns/ical/"/>
+    <B:calendar-description xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:calendar-free-busy-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <D:calendar-order xmlns:D="http://apple.com/ns/ical/"/>
+    <B:calendar-timezone xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:current-user-privilege-set/>
+    <B:default-alarm-vevent-date xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:default-alarm-vevent-datetime xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:displayname/>
+    <C:getctag xmlns:C="http://calendarserver.org/ns/"/>
+    <C:invite xmlns:C="http://calendarserver.org/ns/"/>
+    <D:language-code xmlns:D="http://apple.com/ns/ical/"/>
+    <D:location-code xmlns:D="http://apple.com/ns/ical/"/>
+    <A:owner/>
+    <C:pre-publish-url xmlns:C="http://calendarserver.org/ns/"/>
+    <C:publish-url xmlns:C="http://calendarserver.org/ns/"/>
+    <C:push-transports xmlns:C="http://calendarserver.org/ns/"/>
+    <C:pushkey xmlns:C="http://calendarserver.org/ns/"/>
+    <A:quota-available-bytes/>
+    <A:quota-used-bytes/>
+    <D:refreshrate xmlns:D="http://apple.com/ns/ical/"/>
+    <A:resource-id/>
+    <A:resourcetype/>
+    <B:schedule-calendar-transp xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:schedule-default-calendar-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <C:source xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-alarms xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-attachments xmlns:C="http://calendarserver.org/ns/"/>
+    <C:subscribed-strip-todos xmlns:C="http://calendarserver.org/ns/"/>
+    <B:supported-calendar-component-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:supported-calendar-component-sets xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:supported-report-set/>
+    <A:sync-token/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_notification_depth1_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_notification_depth1_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_notification_depth1_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <A:getetag/>
+    <C:notificationtype xmlns:C="http://calendarserver.org/ns/"/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/post_freebusy.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/post_freebusy.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/post_freebusy.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,13 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:REPLY
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VFREEBUSY
+UID:4288F0F3-5C5B-4DF4-9AD8-B1E5FE3F5B97
+DTSTART:20150804T211500Z
+DTEND:20150804T231500Z
+ATTENDEE:urn:uuid:30000000-0000-0000-0000-000000000005
+DTSTAMP:20150727T203410Z
+ORGANIZER:mailto:user01 at example.com
+END:VFREEBUSY
+END:VCALENDAR
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/principal_search_report.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/principal_search_report.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/principal_search_report.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<C:calendarserver-principal-search xmlns:C="http://calendarserver.org/ns/" context="{context}">
+  {searchTokens}
+  <A:prop xmlns:A="DAV:">
+    <C:email-address-set/>
+    <B:calendar-user-type xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:calendar-user-address-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:principal-URL/>
+    <C:last-name/>
+    <C:record-type/>
+    <A:displayname/>
+    <C:first-name/>
+  </A:prop>
+</C:calendarserver-principal-search>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/report_principal_search.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/report_principal_search.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/report_principal_search.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<C:calendarserver-principal-search xmlns:C="http://calendarserver.org/ns/" context="attendee">
+  <C:search-token>%(search)s</C:search-token>
+  <A:prop xmlns:A="DAV:">
+    <B:calendar-user-type xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <C:email-address-set/>
+    <A:displayname/>
+    <C:first-name/>
+    <C:last-name/>
+    <A:principal-URL/>
+    <C:record-type/>
+    <B:calendar-user-address-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+  </A:prop>
+</C:calendarserver-principal-search>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_color_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_color_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_color_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><D:calendar-color xmlns:D="http://apple.com/ns/ical/" symbolic-color="orange">#FD8208FF</D:calendar-color></A:prop></A:set></A:propertyupdate>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_description_proppatch.request.xml
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_description_proppatch.request.xml	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_description_proppatch.request.xml	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:calendar-description xmlns:B="urn:ietf:params:xml:ns:caldav">some description</B:calendar-description></A:prop></A:set></A:propertyupdate>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_displayname_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_displayname_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_displayname_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><A:displayname>calendar</A:displayname></A:prop></A:set></A:propertyupdate>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_order_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_order_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_order_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><D:calendar-order xmlns:D="http://apple.com/ns/ical/">1</D:calendar-order></A:prop></A:set></A:propertyupdate>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_timezone_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_timezone_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_timezone_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:calendar-timezone xmlns:B="urn:ietf:params:xml:ns:caldav">BEGIN:VCALENDAR&#13;
+VERSION:2.0&#13;
+PRODID:-//Apple Inc.//Mac OS X 10.11//EN&#13;
+CALSCALE:GREGORIAN&#13;
+BEGIN:VTIMEZONE&#13;
+TZID:America/Los_Angeles&#13;
+BEGIN:DAYLIGHT&#13;
+TZOFFSETFROM:-0800&#13;
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU&#13;
+DTSTART:20070311T020000&#13;
+TZNAME:PDT&#13;
+TZOFFSETTO:-0700&#13;
+END:DAYLIGHT&#13;
+BEGIN:STANDARD&#13;
+TZOFFSETFROM:-0700&#13;
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU&#13;
+DTSTART:20071104T020000&#13;
+TZNAME:PST&#13;
+TZOFFSETTO:-0800&#13;
+END:STANDARD&#13;
+END:VTIMEZONE&#13;
+END:VCALENDAR&#13;
+</B:calendar-timezone></A:prop></A:set></A:propertyupdate>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_transparent_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_transparent_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendar_transparent_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:schedule-calendar-transp xmlns:B="urn:ietf:params:xml:ns:caldav"><B:transparent/></B:schedule-calendar-transp></A:prop></A:set></A:propertyupdate>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:schedule-calendar-transp xmlns:B="urn:ietf:params:xml:ns:caldav"><B:opaque/></B:schedule-calendar-transp></A:prop></A:set></A:propertyupdate>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_date_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_date_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_date_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:default-alarm-vevent-date xmlns:B="urn:ietf:params:xml:ns:caldav">BEGIN:VALARM&#13;
+X-WR-ALARMUID:49F29226-D2D7-4464-AE22-0147EDEFB2B4&#13;
+UID:49F29226-D2D7-4464-AE22-0147EDEFB2B4&#13;
+TRIGGER:-PT15H&#13;
+ATTACH;VALUE=URI:Basso&#13;
+ACTION:AUDIO&#13;
+END:VALARM&#13;
+</B:default-alarm-vevent-date></A:prop></A:set></A:propertyupdate>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_datetime_proppatch.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_datetime_proppatch.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_calendarhome_default_alarm_datetime_proppatch.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propertyupdate xmlns:A="DAV:"><A:set><A:prop><B:default-alarm-vevent-datetime xmlns:B="urn:ietf:params:xml:ns:caldav">BEGIN:VALARM&#13;
+X-WR-ALARMUID:4AD03A33-54A6-42BE-A157-47273DD60803&#13;
+UID:4AD03A33-54A6-42BE-A157-47273DD60803&#13;
+TRIGGER;VALUE=DATE-TIME:19760401T005545Z&#13;
+ACTION:NONE&#13;
+END:VALARM&#13;
+</B:default-alarm-vevent-datetime></A:prop></A:set></A:propertyupdate>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_create_calendar.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_create_calendar.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_create_calendar.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<B:mkcalendar xmlns:B="urn:ietf:params:xml:ns:caldav">
+  <A:set xmlns:A="DAV:">
+    <A:prop>
+      <D:calendar-order xmlns:D="http://apple.com/ns/ical/">{order}</D:calendar-order>
+      <B:supported-calendar-component-set>
+        <B:comp name="{component_type}"/>
+      </B:supported-calendar-component-set>
+      <D:calendar-color xmlns:D="http://apple.com/ns/ical/" symbolic-color="custom">#{color}</D:calendar-color>
+      <B:calendar-timezone>BEGIN:VCALENDAR&#13;
+VERSION:2.0&#13;
+PRODID:-//Apple Inc.//Mac OS X 10.11//EN&#13;
+CALSCALE:GREGORIAN&#13;
+BEGIN:VTIMEZONE&#13;
+TZID:America/Los_Angeles&#13;
+BEGIN:DAYLIGHT&#13;
+TZOFFSETFROM:-0800&#13;
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU&#13;
+DTSTART:20070311T020000&#13;
+TZNAME:PDT&#13;
+TZOFFSETTO:-0700&#13;
+END:DAYLIGHT&#13;
+BEGIN:STANDARD&#13;
+TZOFFSETFROM:-0700&#13;
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU&#13;
+DTSTART:20071104T020000&#13;
+TZNAME:PST&#13;
+TZOFFSETTO:-0800&#13;
+END:STANDARD&#13;
+END:VTIMEZONE&#13;
+END:VCALENDAR&#13;
+</B:calendar-timezone>
+      <A:displayname>{name}</A:displayname>
+      <B:schedule-calendar-transp>
+        <B:opaque/>
+      </B:schedule-calendar-transp>
+    </A:prop>
+  </A:set>
+</B:mkcalendar>
\ No newline at end of file

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_delegate_principal_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_delegate_principal_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_delegate_principal_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <B:allowed-calendar-component-set xmlns:B="http://calendarserver.org/ns/"/>
+    <C:calendar-home-set xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+    <C:calendar-user-address-set xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+    <A:current-user-principal/>
+    <A:displayname/>
+    <B:dropbox-home-URL xmlns:B="http://calendarserver.org/ns/"/>
+    <B:email-address-set xmlns:B="http://calendarserver.org/ns/"/>
+    <B:notification-URL xmlns:B="http://calendarserver.org/ns/"/>
+    <A:principal-collection-set/>
+    <A:principal-URL/>
+    <A:resource-id/>
+    <C:schedule-inbox-URL xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+    <C:schedule-outbox-URL xmlns:C="urn:ietf:params:xml:ns:caldav"/>
+    <A:supported-report-set/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_expand.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_expand.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_expand.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:expand-property xmlns:A="DAV:">
+  <A:property name="calendar-proxy-read-for" namespace="http://calendarserver.org/ns/">
+    <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/>
+    <A:property name="email-address-set" namespace="http://calendarserver.org/ns/"/>
+    <A:property name="displayname" namespace="DAV:"/>
+  </A:property>
+  <A:property name="calendar-proxy-write-for" namespace="http://calendarserver.org/ns/">
+    <A:property name="calendar-user-address-set" namespace="urn:ietf:params:xml:ns:caldav"/>
+    <A:property name="email-address-set" namespace="http://calendarserver.org/ns/"/>
+    <A:property name="displayname" namespace="DAV:"/>
+  </A:property>
+</A:expand-property>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_initial_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_initial_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_initial_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <A:current-user-principal/>
+    <A:principal-URL/>
+    <A:resourcetype/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principal_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <B:calendar-home-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:calendar-user-address-set xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:current-user-principal/>
+    <A:displayname/>
+    <C:dropbox-home-URL xmlns:C="http://calendarserver.org/ns/"/>
+    <C:email-address-set xmlns:C="http://calendarserver.org/ns/"/>
+    <C:notification-URL xmlns:C="http://calendarserver.org/ns/"/>
+    <A:principal-collection-set/>
+    <A:principal-URL/>
+    <A:resource-id/>
+    <B:schedule-inbox-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <B:schedule-outbox-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
+    <A:supported-report-set/>
+  </A:prop>
+</A:propfind>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principals_report.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principals_report.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_principals_report.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:principal-search-property-set xmlns:A="DAV:"/>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_query_events_depth1_report.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_query_events_depth1_report.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_query_events_depth1_report.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<B:calendar-query xmlns:B="urn:ietf:params:xml:ns:caldav">
+  <A:prop xmlns:A="DAV:">
+    <A:getetag/>
+    <A:getcontenttype/>
+  </A:prop>
+  <B:filter>
+    <B:comp-filter name="VCALENDAR">
+      <B:comp-filter name="VEVENT">
+        <B:time-range start="20150630T010101Z" end="20150721T010101Z"/>
+      </B:comp-filter>
+    </B:comp-filter>
+  </B:filter>
+</B:calendar-query>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_well_known_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_well_known_propfind.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/startup_well_known_propfind.request	2015-10-05 21:18:27 UTC (rev 15165)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<A:propfind xmlns:A="DAV:">
+  <A:prop>
+    <A:current-user-principal/>
+    <A:principal-URL/>
+    <A:resourcetype/>
+  </A:prop>
+</A:propfind>

Modified: CalendarServer/trunk/contrib/performance/loadtest/sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/sim.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/sim.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -285,7 +285,8 @@
             if 'clientDataSerialization' in config:
                 serializationPath = config['clientDataSerialization']['Path']
                 if not config['clientDataSerialization']['UseOldData']:
-                    shutil.rmtree(serializationPath)
+                    if isdir(serializationPath):
+                        shutil.rmtree(serializationPath)
                 serializationPath = config['clientDataSerialization']['Path']
                 if not isdir(serializationPath):
                     try:
@@ -412,6 +413,7 @@
         return CalendarClientSimulator(
             self.records,
             populator,
+            Random(),
             self.parameters,
             self.reactor,
             self.server,

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -40,38 +40,38 @@
 import os
 
 SIMPLE_EVENT = """\
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0500
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:EDT
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0400
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:EST
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20101018T155431Z
-UID:C98AD237-55AD-4F7D-9009-0D355D835822
-DTEND;TZID=America/New_York:20101021T130000
-TRANSP:OPAQUE
-SUMMARY:Simple event
-DTSTART;TZID=America/New_York:20101021T120000
-DTSTAMP:20101018T155438Z
-SEQUENCE:2
-END:VEVENT
-END:VCALENDAR
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:EDT
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:EST
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20101018T155431Z
+UID:C98AD237-55AD-4F7D-9009-0D355D835822
+DTEND;TZID=America/New_York:20101021T130000
+TRANSP:OPAQUE
+SUMMARY:Simple event
+DTSTART;TZID=America/New_York:20101021T120000
+DTSTAMP:20101018T155438Z
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR
 """
 
 INVITED_EVENT = """\
@@ -347,7 +347,7 @@
     """
     def setUp(self):
         self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
+            AnyUser(), Populator(None), None, None, None, None, None, None)
 
 
     def _simpleAccount(self, userNumber, eventText):
@@ -546,7 +546,7 @@
     """
     def setUp(self):
         self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
+            AnyUser(), Populator(None), None, None, None, None, None, None)
 
 
     def _simpleAccount(self, userNumber, eventText):
@@ -716,7 +716,7 @@
     """
     def setUp(self):
         self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
+            AnyUser(), Populator(None), None, None, None, None, None, None)
 
 
     def test_enabled(self):
@@ -983,7 +983,7 @@
     """
     def setUp(self):
         self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
+            AnyUser(), Populator(None), None, None, None, None, None, None)
 
 
     def test_enabled(self):

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_sim.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_sim.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -121,7 +121,7 @@
         """
         calsim = CalendarClientSimulator(
             [self._user('alice'), self._user('bob'), self._user('carol')],
-            Populator(None), None, None, 'http://example.org:1234/', None, None)
+            Populator(None), None, None, None, 'http://example.org:1234/', None, None)
         users = sorted([
             calsim._createUser(0)[0],
             calsim._createUser(1)[0],
@@ -137,7 +137,7 @@
         """
         calsim = CalendarClientSimulator(
             [self._user('alice')],
-            Populator(None), None, None, 'http://example.org:1234/', None, None)
+            Populator(None), None, None, None, 'http://example.org:1234/', None, None)
         user, auth = calsim._createUser(0)
         self.assertEqual(
             auth['basic'].passwd.find_user_password('Test Realm', 'http://example.org:1234/')[1],
@@ -182,7 +182,7 @@
             [ProfileType(BrokenProfile, {'runResult': profileRunResult})])
         )
         sim = CalendarClientSimulator(
-            [self._user('alice')], Populator(None), params, None, 'http://example.com:1234/', None, None)
+            [self._user('alice')], Populator(None), None, params, None, 'http://example.com:1234/', None, None)
         sim.add(1, 1)
         sim.stop()
         clientRunResult.errback(RuntimeError("Some fictional client problem"))

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-10-05 16:03:26 UTC (rev 15164)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-10-05 21:18:27 UTC (rev 15165)
@@ -85,8 +85,12 @@
             "fullNames": ["cn", ],
             "emailAddresses": ["mail", ],
             "memberDNs": ["uniqueMember", ],
+            "hasCalendars": [],
+            "autoScheduleMode": [],
+            "autoAcceptGroup": [],
             "readWriteProxy": ["icsContact", ],
             "readOnlyProxy": ["icsSecondaryOwners", ],
+            "serviceNodeUID": [],
         },
         "extraFilters": {
             "users": "",
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20151005/e7ace6af/attachment-0001.html>


More information about the calendarserver-changes mailing list