<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11912] CalendarServer/branches/users/cdaboo/json</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/11912">11912</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-11-07 19:33:05 -0800 (Thu, 07 Nov 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge from trunk.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboojsonHACKING">CalendarServer/branches/users/cdaboo/json/HACKING</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonbin_calendarserver_preamblepy">CalendarServer/branches/users/cdaboo/json/bin/_calendarserver_preamble.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarserveraccesslogpy">CalendarServer/branches/users/cdaboo/json/calendarserver/accesslog.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarserverpushamppushpy">CalendarServer/branches/users/cdaboo/json/calendarserver/push/amppush.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarserverpushnotifierpy">CalendarServer/branches/users/cdaboo/json/calendarserver/push/notifier.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarserverpushtesttest_notifierpy">CalendarServer/branches/users/cdaboo/json/calendarserver/push/test/test_notifier.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertapcaldavpy">CalendarServer/branches/users/cdaboo/json/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertaputilpy">CalendarServer/branches/users/cdaboo/json/calendarserver/tap/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertoolsshelldirectorypy">CalendarServer/branches/users/cdaboo/json/calendarserver/tools/shell/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertoolstestdeprovisioncaldavdplist">CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/deprovision/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertoolstestgatewaycaldavdplist">CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/gateway/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertoolstestprincipalscaldavdplist">CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/principals/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncalendarservertoolsupgradepy">CalendarServer/branches/users/cdaboo/json/calendarserver/tools/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthaccountstestxml">CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthaugmentsdtd">CalendarServer/branches/users/cdaboo/json/conf/auth/augments.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconflocalserverstestxml">CalendarServer/branches/users/cdaboo/json/conf/localservers-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconflocalserversxml">CalendarServer/branches/users/cdaboo/json/conf/localservers.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfremoteserverstestxml">CalendarServer/branches/users/cdaboo/json/conf/remoteservers-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfremoteserversxml">CalendarServer/branches/users/cdaboo/json/conf/remoteservers.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfresourcescaldavdresourcesplist">CalendarServer/branches/users/cdaboo/json/conf/resources/caldavd-resources.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestconfigdistplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.dist.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestconfigplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestpopulationpy">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/population.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestsimpy">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/sim.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtesttest_simpy">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/test_sim.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformancesqlusagerequestshttpTestspy">CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/requests/httpTests.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformancesqlusagesqlusagepy">CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/sqlusage.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribtoolsprotocolanalysispy">CalendarServer/branches/users/cdaboo/json/contrib/tools/protocolanalysis.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribtoolsrequest_monitorpy">CalendarServer/branches/users/cdaboo/json/contrib/tools/request_monitor.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonsetuppy">CalendarServer/branches/users/cdaboo/json/setup.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonsupportversionpy">CalendarServer/branches/users/cdaboo/json/support/version.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextenterprisedalsyntaxpy">CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/syntax.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextenterprisedaltesttest_sqlsyntaxpy">CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/test/test_sqlsyntax.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextenterprisefixturespy">CalendarServer/branches/users/cdaboo/json/twext/enterprise/fixtures.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextenterprisequeuepy">CalendarServer/branches/users/cdaboo/json/twext/enterprise/queue.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextenterprisetesttest_queuepy">CalendarServer/branches/users/cdaboo/json/twext/enterprise/test/test_queue.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextpatchespy">CalendarServer/branches/users/cdaboo/json/twext/patches.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextprotocolstesttest_memcachepy">CalendarServer/branches/users/cdaboo/json/twext/protocols/test/test_memcache.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoaggregatepy">CalendarServer/branches/users/cdaboo/json/twext/who/aggregate.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhodirectorypy">CalendarServer/branches/users/cdaboo/json/twext/who/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoexpressionpy">CalendarServer/branches/users/cdaboo/json/twext/who/expression.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoidirectorypy">CalendarServer/branches/users/cdaboo/json/twext/who/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoindexpy">CalendarServer/branches/users/cdaboo/json/twext/who/index.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhotesttest_aggregatepy">CalendarServer/branches/users/cdaboo/json/twext/who/test/test_aggregate.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhotesttest_directorypy">CalendarServer/branches/users/cdaboo/json/twext/who/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhotesttest_utilpy">CalendarServer/branches/users/cdaboo/json/twext/who/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhotesttest_xmlpy">CalendarServer/branches/users/cdaboo/json/twext/who/test/test_xml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoutilpy">CalendarServer/branches/users/cdaboo/json/twext/who/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwextwhoxmlpy">CalendarServer/branches/users/cdaboo/json/twext/who/xml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavconfigpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/config.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryaugmentpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/augment.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorycommonpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/common.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorydirectoryprincipalresourcehtml">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory-principal-resource.html</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorydirectorypy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryidirectorypy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryldapdirectorypy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryresourcepy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestaugmentstestdefaultxml">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test-default.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestaugmentstestxml">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestresourcescaldavdplist">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/resources/caldavd.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_augmentpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_augment.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_directorypy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_ldapdirectorypy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryxmlaugmentsparserpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/xmlaugmentsparser.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavextensionspy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/extensions.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavicalpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavresourcepy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavscheduling_storecaldavresourcepy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavstdconfigpy">CalendarServer/branches/users/cdaboo/json/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavbasedatastoresubpostgrespy">CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/subpostgres.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavbasedatastoreutilpy">CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastorefilepy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/file.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulepy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/schedule.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingaddressmappingpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/addressmapping.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcaldavdeliverypy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/delivery.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcaldavschedulerpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcuaddresspy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/cuaddress.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingimplicitpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduledeliverypy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/delivery.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischedulelocalserverspy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/localservers.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleremoteserverspy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleresourcepy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleschedulerpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduletesttest_deliverypy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduletesttest_localserverspy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingitippy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/itip.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingschedulerpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/scheduler.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingutilspy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/utils.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoresqlpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretestcommonpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretesttest_implicitpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretesttest_utilpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretestutilpy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcaldavicalendardirectoryservicepy">CalendarServer/branches/users/cdaboo/json/txdav/caldav/icalendardirectoryservice.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcarddavdatastoretestcommonpy">CalendarServer/branches/users/cdaboo/json/txdav/carddav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastorefilepy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/file.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresqlpy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemacurrentoracledialectsql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current-oracle-dialect.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemacurrentsql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_19_to_20sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_13_to_14sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_tablespy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_tables.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoretestutilpy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqltesttest_upgradepy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/test/test_upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradepy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradesaddressbook_upgrade_from_1_to_2py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_1_to_2py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradesutilpy">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/util.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthaccountstestpodxml">CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthaugmentstestpodxml">CalendarServer/branches/users/cdaboo/json/conf/auth/augments-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthproxiestestpodxml">CalendarServer/branches/users/cdaboo/json/conf/auth/proxies-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfauthresourcestestpodxml">CalendarServer/branches/users/cdaboo/json/conf/auth/resources-test-pod.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfcaldavdtestpodAplist">CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podA.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfcaldavdtestpodBplist">CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podB.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconflocalserversdtd">CalendarServer/branches/users/cdaboo/json/conf/localservers.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfremoteserversdtd">CalendarServer/branches/users/cdaboo/json/conf/remoteservers.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestclientsplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/clients.plist</a></li>
<li>CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/</li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigseventsonlyplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesacceptsplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyrecurringplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaoldoracledialectv26sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaoldpostgresdialectv26sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_26_to_27sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_26_to_27sql">CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboojsonbincalendarserver_make_partition">CalendarServer/branches/users/cdaboo/json/bin/calendarserver_make_partition</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfcaldavdpartitioningprimaryplist">CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-primary.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfcaldavdpartitioningsecondaryplist">CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-secondary.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfserversdtd">CalendarServer/branches/users/cdaboo/json/conf/servers.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfservertoserverdtd">CalendarServer/branches/users/cdaboo/json/conf/servertoserver.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsonconfsudoersplist">CalendarServer/branches/users/cdaboo/json/conf/sudoers.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigseventsonlyplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesacceptsplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyrecurringplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyplist">CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestsudoersplist">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestsudoers2plist">CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers2.plist</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboojson">CalendarServer/branches/users/cdaboo/json/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboojson"></a>
<div class="propset"><h4>Property changes: CalendarServer/branches/users/cdaboo/json</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11612
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11622-11773
</span><span class="cx">   + /CalDAVTester/trunk:11193-11198
</span><span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11622-11901
</span><a id="CalendarServerbranchesuserscdaboojsonHACKING"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/HACKING (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/HACKING        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/HACKING        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -144,10 +144,6 @@
</span><span class="cx"> 
</span><span class="cx"> PEP-8 items we do not follow:
</span><span class="cx"> 
</span><del>- * Lines need not be limited to 79 spaces, but longer lines are
-   undesirable.  If you can easily do so, try to keep lines under 80
-   columns.
-
</del><span class="cx">  * PEP-8 recommends using a backslash to break long lines up:
</span><span class="cx"> 
</span><span class="cx">    ::
</span><span class="lines">@@ -181,14 +177,6 @@
</span><span class="cx"> 
</span><span class="cx">    Because that's just silly.
</span><span class="cx"> 
</span><del>- * Lining up assignments is OK, within reason:
-
-   ::
-
-     cars       =  4
-     motorbikes =  8
-     bicycles   = 18
-
</del><span class="cx"> Additions:
</span><span class="cx"> 
</span><span class="cx">  * Close parentheses and brackets such as ``()``, ``[]`` and ``{}`` at the
</span><span class="lines">@@ -248,9 +236,8 @@
</span><span class="cx"> 
</span><span class="cx">      process = subprocess.Popen(...)
</span><span class="cx"> 
</span><del>-   This makes code shorter and removes the runtime indirection (which
-   can be relevant in tight loops). It also makes it easier to replace
-   one implementation with another.
</del><ins>+   This makes code shorter and makes it easier to replace one implementation
+   with another.
</ins><span class="cx"> 
</span><span class="cx">  * All files should have an ``__all__`` specification.  Put them at the
</span><span class="cx">    top of the file, before imports (PEP-8 puts them at the top, but
</span><span class="lines">@@ -259,8 +246,8 @@
</span><span class="cx"> 
</span><span class="cx">  * It is more important that symbol names are meaningful than it is
</span><span class="cx">    that they be concise.  ``x`` is rarely an appropriate name for a
</span><del>-   variable.  ``transmogrifierStatus`` is more useful to the reader
-   than ``trmgStat``; avoid contractions.
</del><ins>+   variable.  Avoid contractions: ``transmogrifierStatus`` is more useful
+   to the reader than ``trmgStat``.
</ins><span class="cx"> 
</span><span class="cx">  * A deferred that will be immediately returned may be called ``d``:
</span><span class="cx"> 
</span><span class="lines">@@ -271,9 +258,7 @@
</span><span class="cx">      d.addErrback(onError)
</span><span class="cx">      return d
</span><span class="cx"> 
</span><del>- * We prefer ``inlineCallbacks`` over ``deferredGenerator``.
-   ``inlineCallbacks`` are more readable, and we do not support Python
-   versions old enough that ``deferredGenerator`` would be necessary.
</del><ins>+ * Do not use ``deferredGenerator``.  Use ``inlineCallbacks`` instead.
</ins><span class="cx"> 
</span><span class="cx">  * That said, avoid using ``inlineCallbacks`` when chaining deferreds
</span><span class="cx">    is straightforward, as they are more expensive.  Use
</span><span class="lines">@@ -306,17 +291,29 @@
</span><span class="cx">    Use of underscores is reserved for implied dispatching and the like
</span><span class="cx">    (eg. ``http_FOO()``).  See the Twisted Coding Standard for details.
</span><span class="cx"> 
</span><del>- * Always use a tuple when using ``%``-formatting, even when only one
-   value is being provided:
</del><ins>+ * Do not use ``%``-formatting:
</ins><span class="cx"> 
</span><span class="cx">    ::
</span><span class="cx"> 
</span><span class="cx">      error = &quot;Unexpected value: %s&quot; % (value,)
</span><span class="cx"> 
</span><del>-   Do not use the non-tuple form:
</del><ins>+   Use PEP-3101 formatting instead:
</ins><span class="cx"> 
</span><span class="cx">    ::
</span><span class="cx"> 
</span><ins>+     error = &quot;Unexpected value: {value}&quot;.format(value=value)
+
+ * If you must use ``%``-formatting for some reason, always use a tuple as
+   the format argument, even when only one value is being provided:
+
+   ::
+
+     error = &quot;Unexpected value: %s&quot; % (value,)
+
+   Never use the non-tuple form:
+
+   ::
+
</ins><span class="cx">      error = &quot;Unexpected value: %s&quot; % value
</span><span class="cx"> 
</span><span class="cx">    Which is allowed in Python, but results in a programming error if
</span><span class="lines">@@ -329,8 +326,9 @@
</span><span class="cx">      numbers = (1,2,3,) # No
</span><span class="cx">      numbers = (1,2,3)  # Yes
</span><span class="cx"> 
</span><del>-   It's desirable on multiple lines, though, as that makes re-ordering
-   items easy, and avoids a diff on the last line when adding another:
</del><ins>+   The trailing comma is desirable on multiple lines, though, as that makes
+   re-ordering items easy, and avoids a diff on the last line when adding
+   another:
</ins><span class="cx"> 
</span><span class="cx">    ::
</span><span class="cx"> 
</span><span class="lines">@@ -368,11 +366,11 @@
</span><span class="cx"> ==============
</span><span class="cx"> 
</span><span class="cx">  * If a callable is going to return a Deferred some of the time, it
</span><del>-   should probably return a deferred all of the time.  Return
-   ``succeed(value)`` instead of ``value`` if necessary.  This avoids
-   forcing the caller to check as to whether the value is a deferred
-   or not (eg. by using ``maybeDeferred()``), which is both annoying
-   to code and potentially expensive at runtime.
</del><ins>+   should return a deferred all of the time.  Return ``succeed(value)``
+   instead of ``value`` if necessary.  This avoids forcing the caller
+   to check as to whether the value is a deferred or not (eg. by using
+   ``maybeDeferred()``), which is both annoying to code and potentially
+   expensive at runtime.
</ins><span class="cx"> 
</span><span class="cx">  * Be proactive about closing files and file-like objects.
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonbin_calendarserver_preamblepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/bin/_calendarserver_preamble.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/bin/_calendarserver_preamble.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/bin/_calendarserver_preamble.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -60,7 +60,6 @@
</span><span class="cx">     noConfigOption = [
</span><span class="cx">         &quot;calendarserver_bootstrap_database&quot;,
</span><span class="cx">         &quot;calendarserver_load_augmentdb&quot;,
</span><del>-        &quot;calendarserver_make_partition&quot;,
</del><span class="cx">         &quot;calendarserver_manage_augments&quot;,
</span><span class="cx">         &quot;calendarserver_manage_postgres&quot;,
</span><span class="cx">         &quot;calendarserver_manage_timezones&quot;,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonbincalendarserver_make_partition"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/bin/calendarserver_make_partition (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/bin/calendarserver_make_partition        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/bin/calendarserver_make_partition        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,32 +0,0 @@
</span><del>-#!/usr/bin/env python
-##
-# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-import sys
-
-#PYTHONPATH
-
-if __name__ == &quot;__main__&quot;:
-    if &quot;PYTHONPATH&quot; in globals():
-        sys.path.insert(0, PYTHONPATH)
-    else:
-        try:
-            import _calendarserver_preamble
-        except ImportError:
-            sys.exc_clear()
-
-    from calendarserver.tools.makepartition import main
-    main()
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarserveraccesslogpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/accesslog.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/accesslog.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/accesslog.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -173,7 +173,7 @@
</span><span class="cx">                     formatArgs[&quot;t&quot;] = (nowtime - request.timeStamps[0][1]) * 1000
</span><span class="cx"> 
</span><span class="cx">                 if hasattr(request, &quot;extendedLogItems&quot;):
</span><del>-                    for k, v in request.extendedLogItems.iteritems():
</del><ins>+                    for k, v in sorted(request.extendedLogItems.iteritems(), key=lambda x: x[0]):
</ins><span class="cx">                         k = str(k).replace('&quot;', &quot;%22&quot;)
</span><span class="cx">                         v = str(v).replace('&quot;', &quot;%22&quot;)
</span><span class="cx">                         if &quot; &quot; in v:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarserverpushamppushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/push/amppush.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/push/amppush.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/push/amppush.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -48,7 +48,8 @@
</span><span class="cx"> # AMP Commands sent to client (and forwarded to Master)
</span><span class="cx"> 
</span><span class="cx"> class NotificationForID(amp.Command):
</span><del>-    arguments = [('id', amp.String()), ('dataChangedTimestamp', amp.Integer())]
</del><ins>+    arguments = [('id', amp.String()),
+                 ('dataChangedTimestamp', amp.Integer(optional=True))]
</ins><span class="cx">     response = [('status', amp.String())]
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarserverpushnotifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/push/notifier.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/push/notifier.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/push/notifier.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -84,10 +84,13 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def notify(self):
</del><ins>+    def notify(self, txn):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Send the notification. For a home object we just push using the home id. For a home
</span><span class="cx">         child we push both the owner home id and the owned home child id.
</span><ins>+
+        @param txn: The transaction to create the work item with
+        @type txn: L{CommonStoreTransaction}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         # Push ids from the store objects are a tuple of (prefix, name,) and we need to compose that
</span><span class="cx">         # into a single token.
</span><span class="lines">@@ -100,7 +103,7 @@
</span><span class="cx">         for prefix, id in ids:
</span><span class="cx">             if self._notify:
</span><span class="cx">                 self.log.debug(&quot;Notifications are enabled: %s %s/%s&quot; % (self._storeObject, prefix, id,))
</span><del>-                yield self._notifierFactory.send(prefix, id)
</del><ins>+                yield self._notifierFactory.send(prefix, id, txn)
</ins><span class="cx">             else:
</span><span class="cx">                 self.log.debug(&quot;Skipping notification for: %s %s/%s&quot; % (self._storeObject, prefix, id,))
</span><span class="cx"> 
</span><span class="lines">@@ -147,11 +150,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def send(self, prefix, id):
-        txn = self.store.newTransaction()
</del><ins>+    def send(self, prefix, id, txn):
+        &quot;&quot;&quot;
+        Enqueue a push notification work item on the provided transaction.
+        &quot;&quot;&quot;
</ins><span class="cx">         notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=self.coalesceSeconds)
</span><span class="cx">         yield txn.enqueue(PushNotificationWork, pushID=self.pushKeyForId(prefix, id), notBefore=notBefore)
</span><del>-        yield txn.commit()
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def newNotifier(self, storeObject):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarserverpushtesttest_notifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/push/test/test_notifier.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/push/test/test_notifier.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/push/test/test_notifier.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -169,8 +169,8 @@
</span><span class="cx"> 
</span><span class="cx">         home = yield self.homeUnderTest()
</span><span class="cx">         yield home.notifyChanged()
</span><ins>+        self.assertEquals(self.notifierFactory.history, [&quot;/CalDAV/example.com/home1/&quot;])
</ins><span class="cx">         yield self.commit()
</span><del>-        self.assertEquals(self.notifierFactory.history, [&quot;/CalDAV/example.com/home1/&quot;])
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -178,11 +178,11 @@
</span><span class="cx"> 
</span><span class="cx">         calendar = yield self.calendarUnderTest()
</span><span class="cx">         yield calendar.notifyChanged()
</span><del>-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([&quot;/CalDAV/example.com/home1/&quot;, &quot;/CalDAV/example.com/home1/calendar_1/&quot;])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -191,7 +191,6 @@
</span><span class="cx">         calendar = yield self.calendarUnderTest()
</span><span class="cx">         home2 = yield self.homeUnderTest(name=&quot;home2&quot;)
</span><span class="cx">         yield calendar.shareWith(home2, _BIND_MODE_WRITE)
</span><del>-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -200,11 +199,11 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home2/&quot;
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         calendar = yield self.calendarUnderTest()
</span><span class="cx">         home2 = yield self.homeUnderTest(name=&quot;home2&quot;)
</span><span class="cx">         yield calendar.unshareWith(home2)
</span><del>-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -213,6 +212,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home2/&quot;
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -226,11 +226,11 @@
</span><span class="cx"> 
</span><span class="cx">         shared = yield self.calendarUnderTest(home=&quot;home2&quot;, name=shareName)
</span><span class="cx">         yield shared.notifyChanged()
</span><del>-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([&quot;/CalDAV/example.com/home1/&quot;, &quot;/CalDAV/example.com/home1/calendar_1/&quot;])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -238,8 +238,8 @@
</span><span class="cx"> 
</span><span class="cx">         notifications = yield self.transactionUnderTest().notificationsWithUID(&quot;home1&quot;)
</span><span class="cx">         yield notifications.notifyChanged()
</span><del>-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([&quot;/CalDAV/example.com/home1/&quot;, &quot;/CalDAV/example.com/home1/notification/&quot;])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tap/caldav.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tap/caldav.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tap/caldav.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -401,6 +401,14 @@
</span><span class="cx">         print(&quot;Reading configuration from file: %s&quot; % (self[&quot;config&quot;],))
</span><span class="cx"> 
</span><span class="cx">         config.load(self[&quot;config&quot;])
</span><ins>+
+        for path in config.getProvider().importedFiles:
+            print(&quot;Imported configuration from file: '%s'&quot; % (path,))
+        for path in config.getProvider().includedFiles:
+            print(&quot;Adding configuration from file: '%s'&quot; % (path,))
+        for path in config.getProvider().missingFiles:
+            print(&quot;Missing configuration file: '%s'&quot; % (path,))
+
</ins><span class="cx">         config.updateDefaults(self.overrides)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1212,6 +1220,28 @@
</span><span class="cx">             else:
</span><span class="cx">                 groupCacher = None
</span><span class="cx"> 
</span><ins>+            # Optionally enable Manhole access
+            if config.Manhole.Enabled:
+                try:
+                    from twisted.conch.manhole_tap import makeService as manholeMakeService
+                    portString = &quot;tcp:%d:interface=127.0.0.1&quot; % (config.Manhole.StartingPortNumber,)
+                    manholeService = manholeMakeService({
+                        &quot;sshPort&quot; : None,
+                        &quot;telnetPort&quot; : portString,
+                        &quot;namespace&quot; : {
+                            &quot;config&quot; : config,
+                            &quot;service&quot; : result,
+                            &quot;store&quot; : store,
+                            &quot;directory&quot; : directory,
+                            },
+                        &quot;passwd&quot; : config.Manhole.PasswordFilePath,
+                    })
+                    manholeService.setServiceParent(result)
+                    # Using print(because logging isn't ready at this point)
+                    print(&quot;Manhole access enabled: %s&quot; % (portString,))
+                except ImportError:
+                    print(&quot;Manhole access could not enabled because manhole_tap could not be imported&quot;)
+
</ins><span class="cx">             def decorateTransaction(txn):
</span><span class="cx">                 txn._pushDistributor = pushDistributor
</span><span class="cx">                 txn._rootResource = result.rootResource
</span><span class="lines">@@ -1407,7 +1437,9 @@
</span><span class="cx"> 
</span><span class="cx">                 # Conditionally stop after upgrade at this point
</span><span class="cx">                 pps.addStep(
</span><del>-                    QuitAfterUpgradeStep(config.StopAfterUpgradeTriggerFile)
</del><ins>+                    QuitAfterUpgradeStep(
+                        config.StopAfterUpgradeTriggerFile or config.UpgradeHomePrefix
+                    )
</ins><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx">                 pps.addStep(
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertaputilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tap/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tap/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tap/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -95,6 +95,7 @@
</span><span class="cx"> from txdav.common.datastore.sql import CommonDataStore as CommonSQLDataStore
</span><span class="cx"> from txdav.common.datastore.file import CommonDataStore as CommonFileDataStore
</span><span class="cx"> from txdav.common.datastore.sql import current_sql_schema
</span><ins>+from txdav.common.datastore.upgrade.sql.upgrade import NotAllowedToUpgrade
</ins><span class="cx"> from twext.python.filepath import CachingFilePath
</span><span class="cx"> from urllib import quote
</span><span class="cx"> from twisted.python.usage import UsageError
</span><span class="lines">@@ -635,11 +636,23 @@
</span><span class="cx">             addSystemEventTrigger(&quot;after&quot;, &quot;startup&quot;, timezoneStdService.onStartup)
</span><span class="cx"> 
</span><span class="cx">     #
</span><del>-    # iSchedule service
</del><ins>+    # iSchedule service for podding
</ins><span class="cx">     #
</span><ins>+    if config.Servers.Enabled:
+        log.info(&quot;Setting up iSchedule podding inbox resource: {cls}&quot;, cls=iScheduleResourceClass)
+
+        ischedule = iScheduleResourceClass(
+            root,
+            newStore,
+            podding=True
+        )
+        root.putChild(config.Servers.InboxName, ischedule)
+
+    #
+    # iSchedule service (not used for podding)
+    #
</ins><span class="cx">     if config.Scheduling.iSchedule.Enabled:
</span><del>-        log.info(&quot;Setting up iSchedule inbox resource: {cls}&quot;,
-                      cls=iScheduleResourceClass)
</del><ins>+        log.info(&quot;Setting up iSchedule inbox resource: {cls}&quot;, cls=iScheduleResourceClass)
</ins><span class="cx"> 
</span><span class="cx">         ischedule = iScheduleResourceClass(
</span><span class="cx">             root,
</span><span class="lines">@@ -650,8 +663,7 @@
</span><span class="cx">         # Do DomainKey resources
</span><span class="cx">         DKIMUtils.validConfiguration(config)
</span><span class="cx">         if config.Scheduling.iSchedule.DKIM.Enabled:
</span><del>-            log.info(&quot;Setting up domainkey resource: {res}&quot;,
-                res=DomainKeyResource)
</del><ins>+            log.info(&quot;Setting up domainkey resource: {res}&quot;, res=DomainKeyResource)
</ins><span class="cx">             domain = config.Scheduling.iSchedule.DKIM.Domain if config.Scheduling.iSchedule.DKIM.Domain else config.ServerHostName
</span><span class="cx">             dk = DomainKeyResource(
</span><span class="cx">                 domain,
</span><span class="lines">@@ -1077,7 +1089,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def defaultStepWithFailure(self, failure):
</span><del>-        log.failure(&quot;Step failure&quot;, failure=failure)
</del><ins>+        if failure.type != NotAllowedToUpgrade:
+            log.failure(&quot;Step failure&quot;, failure=failure)
</ins><span class="cx">         return failure
</span><span class="cx"> 
</span><span class="cx">     # def protectStep(self, callback):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertoolsshelldirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tools/shell/directory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tools/shell/directory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tools/shell/directory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx">     returnValue(sorted(records, key=operator.attrgetter(&quot;fullName&quot;)))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordInfo(directory, record):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -58,8 +59,9 @@
</span><span class="cx">     add(&quot;Proxy access&quot;     , (yield recordProxyAccessInfo(directory, record)))
</span><span class="cx"> 
</span><span class="cx">     returnValue(&quot;\n&quot;.join(info))
</span><del>-        
</del><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> def recordBasicInfo(directory, record):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Basic information for a record.
</span><span class="lines">@@ -87,15 +89,15 @@
</span><span class="cx">     for cua in record.calendarUserAddresses:
</span><span class="cx">         add(&quot;Calendar User Address&quot;, cua)
</span><span class="cx"> 
</span><del>-    add(&quot;Server ID&quot;           , record.serverID              )
-    add(&quot;Partition ID&quot;        , record.partitionID           )
-    add(&quot;Enabled&quot;             , record.enabled               )
-    add(&quot;Enabled for Calendar&quot;, record.enabledForCalendaring )
</del><ins>+    add(&quot;Server ID&quot;           , record.serverID)
+    add(&quot;Enabled&quot;             , record.enabled)
+    add(&quot;Enabled for Calendar&quot;, record.enabledForCalendaring)
</ins><span class="cx">     add(&quot;Enabled for Contacts&quot;, record.enabledForAddressBooks)
</span><span class="cx"> 
</span><span class="cx">     return succeed(table.toString())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def recordGroupMembershipInfo(directory, record):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Group membership info for a record.
</span><span class="lines">@@ -109,7 +111,7 @@
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><span class="cx">     rows = sorted(rows,
</span><del>-        key = lambda row: (row[1], row[2])
</del><ins>+        key=lambda row: (row[1], row[2])
</ins><span class="cx">     )
</span><span class="cx"> 
</span><span class="cx">     table = Table()
</span><span class="lines">@@ -120,6 +122,7 @@
</span><span class="cx">     return succeed(table.toString())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def recordProxyAccessInfo(directory, record):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -154,7 +157,7 @@
</span><span class="cx">         returnValue(None)
</span><span class="cx"> 
</span><span class="cx">     rows = sorted(rows,
</span><del>-        key = lambda row: (row[1], row[2], row[4])
</del><ins>+        key=lambda row: (row[1], row[2], row[4])
</ins><span class="cx">     )
</span><span class="cx"> 
</span><span class="cx">     table = Table()
</span><span class="lines">@@ -165,6 +168,7 @@
</span><span class="cx">     returnValue(table.toString())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def summarizeRecords(directory, records):
</span><span class="cx">     table = Table()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertoolstestdeprovisioncaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/deprovision/caldavd.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/deprovision/caldavd.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/deprovision/caldavd.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -191,7 +191,6 @@
</span><span class="cx">         Augments for the directory service records to add calendar specific attributes.
</span><span class="cx"> 
</span><span class="cx">         A variety of augment services are available for use.
</span><del>-        When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx">       --&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;!-- XML File Augment Service --&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertoolstestgatewaycaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/gateway/caldavd.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/gateway/caldavd.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/gateway/caldavd.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -203,7 +203,6 @@
</span><span class="cx">         Augments for the directory service records to add calendar specific attributes.
</span><span class="cx"> 
</span><span class="cx">         A variety of augment services are available for use.
</span><del>-        When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx">       --&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;!-- XML File Augment Service --&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertoolstestprincipalscaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/principals/caldavd.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/principals/caldavd.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tools/test/principals/caldavd.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -195,7 +195,6 @@
</span><span class="cx">         Augments for the directory service records to add calendar specific attributes.
</span><span class="cx"> 
</span><span class="cx">         A variety of augment services are available for use.
</span><del>-        When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx">       --&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;!-- XML File Augment Service --&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncalendarservertoolsupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/calendarserver/tools/upgrade.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/calendarserver/tools/upgrade.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/calendarserver/tools/upgrade.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx"> 
</span><span class="cx">     optParameters = [
</span><span class="cx">         ['config', 'f', DEFAULT_CONFIG_FILE, &quot;Specify caldavd.plist configuration path.&quot;],
</span><ins>+        ['prefix', 'x', &quot;&quot;, &quot;Only upgrade homes with the specified GUID prefix - partial upgrade only.&quot;],
</ins><span class="cx">     ]
</span><span class="cx"> 
</span><span class="cx">     def __init__(self):
</span><span class="lines">@@ -142,11 +143,17 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Immediately stop.  The upgrade will have been run before this.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        # If we get this far the database is OK
-        if self.options[&quot;status&quot;]:
-            self.output.write(&quot;Database OK.\n&quot;)
</del><ins>+        if self.store is None:
+            if self.options[&quot;status&quot;]:
+                self.output.write(&quot;Upgrade needed.\n&quot;)
+            else:
+                self.output.write(&quot;Upgrade failed.\n&quot;)
</ins><span class="cx">         else:
</span><del>-            self.output.write(&quot;Upgrade complete, shutting down.\n&quot;)
</del><ins>+            # If we get this far the database is OK
+            if self.options[&quot;status&quot;]:
+                self.output.write(&quot;Database OK.\n&quot;)
+            else:
+                self.output.write(&quot;Upgrade complete, shutting down.\n&quot;)
</ins><span class="cx">         UpgraderService.started = True
</span><span class="cx"> 
</span><span class="cx">         from twisted.internet import reactor
</span><span class="lines">@@ -191,9 +198,11 @@
</span><span class="cx">             data.MergeUpgrades = True
</span><span class="cx">         config.addPostUpdateHooks([setMerge])
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def makeService(store):
</span><span class="cx">         return UpgraderService(store, options, output, reactor, config)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def onlyUpgradeEvents(eventDict):
</span><span class="cx">         text = formatEvent(eventDict)
</span><span class="cx">         output.write(logDateString() + &quot; &quot; + text + &quot;\n&quot;)
</span><span class="lines">@@ -203,14 +212,19 @@
</span><span class="cx">         log.publisher.levels.setLogLevelForNamespace(None, LogLevel.debug)
</span><span class="cx">         addObserver(onlyUpgradeEvents)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def customServiceMaker():
</span><span class="cx">         customService = CalDAVServiceMaker()
</span><span class="cx">         customService.doPostImport = options[&quot;postprocess&quot;]
</span><span class="cx">         return customService
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _patchConfig(config):
</span><span class="cx">         config.FailIfUpgradeNeeded = options[&quot;status&quot;]
</span><ins>+        if options[&quot;prefix&quot;]:
+            config.UpgradeHomePrefix = options[&quot;prefix&quot;]
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _onShutdown():
</span><span class="cx">         if not UpgraderService.started:
</span><span class="cx">             print(&quot;Failed to start service.&quot;)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthaccountstestpodxmlfromrev11901CalendarServertrunkconfauthaccountstestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test-pod.xml (from rev 11901, CalendarServer/trunk/conf/auth/accounts-test-pod.xml) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test-pod.xml                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test-pod.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+
+&lt;accounts realm=&quot;Test Realm&quot;&gt;
+  &lt;user&gt;
+    &lt;uid&gt;admin&lt;/uid&gt;
+    &lt;guid&gt;admin&lt;/guid&gt;
+    &lt;password&gt;admin&lt;/password&gt;
+    &lt;name&gt;Super User&lt;/name&gt;
+    &lt;first-name&gt;Super&lt;/first-name&gt;
+    &lt;last-name&gt;User&lt;/last-name&gt;
+  &lt;/user&gt;
+  &lt;user repeat=&quot;101&quot;&gt;
+    &lt;uid&gt;user%02d&lt;/uid&gt;
+    &lt;uid&gt;User %02d&lt;/uid&gt;
+    &lt;guid&gt;user%02d&lt;/guid&gt;
+    &lt;password&gt;user%02d&lt;/password&gt;
+    &lt;name&gt;User %02d&lt;/name&gt;
+    &lt;first-name&gt;User&lt;/first-name&gt;
+    &lt;last-name&gt;%02d&lt;/last-name&gt;
+    &lt;email-address&gt;user%02d@example.com&lt;/email-address&gt;
+  &lt;/user&gt;
+  &lt;user repeat=&quot;101&quot;&gt;
+    &lt;uid&gt;puser%02d&lt;/uid&gt;
+    &lt;uid&gt;Puser %02d&lt;/uid&gt;
+    &lt;guid&gt;puser%02d&lt;/guid&gt;
+    &lt;password&gt;puser%02d&lt;/password&gt;
+    &lt;name&gt;Puser %02d&lt;/name&gt;
+    &lt;first-name&gt;Puser&lt;/first-name&gt;
+    &lt;last-name&gt;%02d&lt;/last-name&gt;
+    &lt;email-address&gt;puser%02d@example.com&lt;/email-address&gt;
+  &lt;/user&gt;
+&lt;/accounts&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthaccountstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/accounts-test.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -89,7 +89,7 @@
</span><span class="cx">     &lt;first-name&gt;ま&lt;/first-name&gt;
</span><span class="cx">     &lt;last-name&gt;だ&lt;/last-name&gt;
</span><span class="cx">   &lt;/user&gt;
</span><del>-  &lt;user repeat=&quot;99&quot;&gt;
</del><ins>+  &lt;user repeat=&quot;101&quot;&gt;
</ins><span class="cx">     &lt;uid&gt;user%02d&lt;/uid&gt;
</span><span class="cx">     &lt;uid&gt;User %02d&lt;/uid&gt;
</span><span class="cx">     &lt;guid&gt;user%02d&lt;/guid&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthaugmentstestpodxmlfromrev11901CalendarServertrunkconfauthaugmentstestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/auth/augments-test-pod.xml (from rev 11901, CalendarServer/trunk/conf/auth/augments-test-pod.xml) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/augments-test-pod.xml                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/augments-test-pod.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+&lt;!DOCTYPE augments SYSTEM &quot;augments.dtd&quot;&gt;
+
+&lt;augments&gt;
+  &lt;record&gt;
+    &lt;uid&gt;Default&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;server-id&gt;A&lt;/server-id&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+  &lt;record repeat=&quot;101&quot;&gt;
+    &lt;uid&gt;puser%02d&lt;/uid&gt;
+    &lt;enable&gt;true&lt;/enable&gt;
+    &lt;server-id&gt;B&lt;/server-id&gt;
+    &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
+    &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
+  &lt;/record&gt;
+&lt;/augments&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthaugmentsdtd"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/auth/augments.dtd (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/augments.dtd        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/augments.dtd        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -19,7 +19,7 @@
</span><span class="cx">   &lt;!ELEMENT record (
</span><span class="cx">                   uid,
</span><span class="cx">                   enable,
</span><del>-                  (server-id, partition-id?)?,
</del><ins>+                  server-id?,
</ins><span class="cx">                   enable-calendar?,
</span><span class="cx">                   enable-addressbook?,
</span><span class="cx">                   enable-login?,
</span><span class="lines">@@ -32,7 +32,6 @@
</span><span class="cx">   &lt;!ELEMENT uid                (#PCDATA)&gt;
</span><span class="cx">   &lt;!ELEMENT enable             (#PCDATA)&gt;
</span><span class="cx">   &lt;!ELEMENT server-id          (#PCDATA)&gt;
</span><del>-  &lt;!ELEMENT partition-id       (#PCDATA)&gt;
</del><span class="cx">   &lt;!ELEMENT enable-calendar    (#PCDATA)&gt;
</span><span class="cx">   &lt;!ELEMENT enable-addressbook (#PCDATA)&gt;
</span><span class="cx">   &lt;!ELEMENT enable-login       (#PCDATA)&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthproxiestestpodxmlfromrev11901CalendarServertrunkconfauthproxiestestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/auth/proxies-test-pod.xml (from rev 11901, CalendarServer/trunk/conf/auth/proxies-test-pod.xml) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/proxies-test-pod.xml                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/proxies-test-pod.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,22 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+
+&lt;!--
+Copyright (c) 2009-2013 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ --&gt;
+
+&lt;!DOCTYPE proxies SYSTEM &quot;proxies.dtd&quot;&gt;
+
+&lt;proxies&gt;
+&lt;/proxies&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfauthresourcestestpodxmlfromrev11901CalendarServertrunkconfauthresourcestestpodxml"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/auth/resources-test-pod.xml (from rev 11901, CalendarServer/trunk/conf/auth/resources-test-pod.xml) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/auth/resources-test-pod.xml                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/auth/resources-test-pod.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,4 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+
+&lt;accounts realm=&quot;Test Realm&quot;&gt;
+&lt;/accounts&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfcaldavdpartitioningprimaryplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-primary.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-primary.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-primary.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,85 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-  &lt;dict&gt;
-
-    &lt;!--  Servers --&gt;
-    &lt;key&gt;Servers&lt;/key&gt;
-    &lt;dict&gt;
-            &lt;key&gt;Enabled&lt;/key&gt;
-            &lt;true/&gt;
-            &lt;key&gt;ConfigFile&lt;/key&gt;
-            &lt;string&gt;localservers.xml&lt;/string&gt;
-            &lt;key&gt;MaxClients&lt;/key&gt;
-            &lt;integer&gt;5&lt;/integer&gt;
-        &lt;/dict&gt;
-    &lt;key&gt;ServerPartitionID&lt;/key&gt;
-    &lt;string&gt;00001&lt;/string&gt;
-
-    &lt;!-- PostgreSQL ProxyDB Service --&gt;
-    &lt;key&gt;ProxyDBService&lt;/key&gt;
-    &lt;dict&gt;
-      &lt;key&gt;type&lt;/key&gt;
-      &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
-      
-      &lt;key&gt;params&lt;/key&gt;
-      &lt;dict&gt;
-        &lt;key&gt;host&lt;/key&gt;
-        &lt;string&gt;localhost&lt;/string&gt;
-        &lt;key&gt;database&lt;/key&gt;
-        &lt;string&gt;proxies&lt;/string&gt;
-      &lt;/dict&gt;
-    &lt;/dict&gt;
-
-    &lt;!-- Support for Memcached --&gt;
-    &lt;key&gt;Memcached&lt;/key&gt;
-    &lt;dict&gt;
-          &lt;key&gt;Pools&lt;/key&gt;
-                &lt;dict&gt;
-                  &lt;key&gt;CommonToAllNodes&lt;/key&gt;
-                  &lt;dict&gt;
-                    &lt;key&gt;ClientEnabled&lt;/key&gt;
-                    &lt;true/&gt;
-                    &lt;key&gt;ServerEnabled&lt;/key&gt;
-                    &lt;true/&gt;
-                    &lt;key&gt;BindAddress&lt;/key&gt;
-                    &lt;string&gt;localhost&lt;/string&gt;
-                    &lt;key&gt;Port&lt;/key&gt;
-                    &lt;integer&gt;11311&lt;/integer&gt;
-                    &lt;key&gt;HandleCacheTypes&lt;/key&gt;
-                    &lt;array&gt;
-                      &lt;string&gt;ProxyDB&lt;/string&gt;
-                      &lt;string&gt;PrincipalToken&lt;/string&gt;
-                      &lt;string&gt;DIGESTCREDENTIALS&lt;/string&gt;
-                    &lt;/array&gt;
-                  &lt;/dict&gt;
-                &lt;/dict&gt;
-      &lt;key&gt;MaxClients&lt;/key&gt;
-      &lt;integer&gt;5&lt;/integer&gt;
-      &lt;key&gt;memcached&lt;/key&gt;
-      &lt;string&gt;../memcached/_root/bin/memcached&lt;/string&gt; &lt;!-- Find in PATH --&gt;
-      &lt;key&gt;Options&lt;/key&gt;
-      &lt;array&gt;
-        &lt;!--&lt;string&gt;-vv&lt;/string&gt;--&gt;
-      &lt;/array&gt;
-    &lt;/dict&gt;
-
-  &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfcaldavdpartitioningsecondaryplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-secondary.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-secondary.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/caldavd-partitioning-secondary.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,85 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-  &lt;dict&gt;
-
-    &lt;!--  Servers --&gt;
-    &lt;key&gt;Servers&lt;/key&gt;
-    &lt;dict&gt;
-            &lt;key&gt;Enabled&lt;/key&gt;
-            &lt;true/&gt;
-            &lt;key&gt;ConfigFile&lt;/key&gt;
-            &lt;string&gt;localservers.xml&lt;/string&gt;
-            &lt;key&gt;MaxClients&lt;/key&gt;
-            &lt;integer&gt;5&lt;/integer&gt;
-        &lt;/dict&gt;
-    &lt;key&gt;ServerPartitionID&lt;/key&gt;
-    &lt;string&gt;00002&lt;/string&gt;
-
-    &lt;!-- PostgreSQL ProxyDB Service --&gt;
-    &lt;key&gt;ProxyDBService&lt;/key&gt;
-    &lt;dict&gt;
-      &lt;key&gt;type&lt;/key&gt;
-      &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
-      
-      &lt;key&gt;params&lt;/key&gt;
-      &lt;dict&gt;
-        &lt;key&gt;host&lt;/key&gt;
-        &lt;string&gt;localhost&lt;/string&gt;
-        &lt;key&gt;database&lt;/key&gt;
-        &lt;string&gt;proxies&lt;/string&gt;
-      &lt;/dict&gt;
-    &lt;/dict&gt;
-
-    &lt;!-- Support for Memcached --&gt;
-    &lt;key&gt;Memcached&lt;/key&gt;
-    &lt;dict&gt;
-          &lt;key&gt;Pools&lt;/key&gt;
-                &lt;dict&gt;
-                  &lt;key&gt;CommonToAllNodes&lt;/key&gt;
-                  &lt;dict&gt;
-                    &lt;key&gt;ClientEnabled&lt;/key&gt;
-                    &lt;true/&gt;
-                    &lt;key&gt;ServerEnabled&lt;/key&gt;
-                    &lt;false/&gt;
-                    &lt;key&gt;BindAddress&lt;/key&gt;
-                    &lt;string&gt;localhost&lt;/string&gt;
-                    &lt;key&gt;Port&lt;/key&gt;
-                    &lt;integer&gt;11311&lt;/integer&gt;
-                    &lt;key&gt;HandleCacheTypes&lt;/key&gt;
-                    &lt;array&gt;
-                      &lt;string&gt;ProxyDB&lt;/string&gt;
-                      &lt;string&gt;PrincipalToken&lt;/string&gt;
-                      &lt;string&gt;DIGESTCREDENTIALS&lt;/string&gt;
-                    &lt;/array&gt;
-                  &lt;/dict&gt;
-                &lt;/dict&gt;
-      &lt;key&gt;MaxClients&lt;/key&gt;
-      &lt;integer&gt;5&lt;/integer&gt;
-      &lt;key&gt;memcached&lt;/key&gt;
-      &lt;string&gt;../memcached/_root/bin/memcached&lt;/string&gt; &lt;!-- Find in PATH --&gt;
-      &lt;key&gt;Options&lt;/key&gt;
-      &lt;array&gt;
-        &lt;!--&lt;string&gt;-vv&lt;/string&gt;--&gt;
-      &lt;/array&gt;
-    &lt;/dict&gt;
-
-  &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfcaldavdtestpodAplistfromrev11901CalendarServertrunkconfcaldavdtestpodAplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podA.plist (from rev 11901, CalendarServer/trunk/conf/caldavd-test-podA.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podA.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podA.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,158 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+  &lt;dict&gt;
+
+    &lt;!-- Import a parent config before this one --&gt;
+    &lt;key&gt;ImportConfig&lt;/key&gt;
+    &lt;string&gt;./conf/caldavd-test.plist&lt;/string&gt;
+
+    &lt;!-- HTTP port [0 = disable HTTP] --&gt;
+    &lt;key&gt;HTTPPort&lt;/key&gt;
+    &lt;integer&gt;8008&lt;/integer&gt;
+
+    &lt;!-- SSL port [0 = disable HTTPS] --&gt;
+    &lt;!-- (Must also configure SSLCertificate and SSLPrivateKey below) --&gt;
+    &lt;key&gt;SSLPort&lt;/key&gt;
+    &lt;integer&gt;8443&lt;/integer&gt;
+
+    &lt;!-- List of port numbers to bind to for HTTP [empty = same as &quot;Port&quot;] --&gt;
+    &lt;key&gt;BindHTTPPorts&lt;/key&gt;
+    &lt;array&gt;
+    &lt;/array&gt;
+
+    &lt;!-- List of port numbers to bind to for SSL [empty = same as &quot;SSLPort&quot;] --&gt;
+    &lt;key&gt;BindSSLPorts&lt;/key&gt;
+    &lt;array&gt;
+    &lt;/array&gt;
+
+    &lt;!-- Server root --&gt;
+    &lt;key&gt;ServerRoot&lt;/key&gt;
+    &lt;string&gt;./data/podA&lt;/string&gt;
+
+    &lt;!-- Configuration root --&gt;
+    &lt;key&gt;ConfigRoot&lt;/key&gt;
+    &lt;string&gt;./conf&lt;/string&gt;
+
+    &lt;!-- XML File Directory Service --&gt;
+    &lt;key&gt;DirectoryService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFile&lt;/key&gt;
+        &lt;string&gt;./conf/auth/accounts-test-pod.xml&lt;/string&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+    
+    &lt;!-- Resource and Location Service --&gt;
+    &lt;key&gt;ResourceService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;Enabled&lt;/key&gt;
+      &lt;true/&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFile&lt;/key&gt;
+        &lt;string&gt;./conf/auth/resources-test-pod.xml&lt;/string&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+
+    &lt;!-- XML File Augment Service --&gt;
+    &lt;key&gt;AugmentService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFiles&lt;/key&gt;
+        &lt;array&gt;
+              &lt;string&gt;./conf/auth/augments-test-pod.xml&lt;/string&gt;
+        &lt;/array&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+
+    &lt;key&gt;ProxyLoadFromFile&lt;/key&gt;
+    &lt;string&gt;./conf/auth/proxies-test-pod.xml&lt;/string&gt;
+
+    &lt;!--  Servers --&gt;
+    &lt;key&gt;Servers&lt;/key&gt;
+    &lt;dict&gt;
+            &lt;key&gt;Enabled&lt;/key&gt;
+            &lt;true/&gt;
+            &lt;key&gt;ConfigFile&lt;/key&gt;
+            &lt;string&gt;./conf/localservers-test.xml&lt;/string&gt;
+            &lt;key&gt;MaxClients&lt;/key&gt;
+            &lt;integer&gt;5&lt;/integer&gt;
+            &lt;key&gt;InboxName&lt;/key&gt;
+            &lt;string&gt;podding&lt;/string&gt;
+        &lt;/dict&gt;
+
+    &lt;!-- Support for Memcached --&gt;
+    &lt;key&gt;Memcached&lt;/key&gt;
+    &lt;dict&gt;
+          &lt;key&gt;Pools&lt;/key&gt;
+                &lt;dict&gt;
+                  &lt;key&gt;Default&lt;/key&gt;
+                  &lt;dict&gt;
+                    &lt;key&gt;ClientEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;ServerEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;BindAddress&lt;/key&gt;
+                    &lt;string&gt;localhost&lt;/string&gt;
+                    &lt;key&gt;Port&lt;/key&gt;
+                    &lt;integer&gt;11211&lt;/integer&gt;
+                  &lt;/dict&gt;
+                  &lt;key&gt;ProxyDB&lt;/key&gt;
+                  &lt;dict&gt;
+                    &lt;key&gt;ClientEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;ServerEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;BindAddress&lt;/key&gt;
+                    &lt;string&gt;localhost&lt;/string&gt;
+                    &lt;key&gt;Port&lt;/key&gt;
+                    &lt;integer&gt;11311&lt;/integer&gt;
+                    &lt;key&gt;HandleCacheTypes&lt;/key&gt;
+                    &lt;array&gt;
+                      &lt;string&gt;ProxyDB&lt;/string&gt;
+                      &lt;string&gt;PrincipalToken&lt;/string&gt;
+                      &lt;string&gt;DIGESTCREDENTIALS&lt;/string&gt;
+                    &lt;/array&gt;
+                  &lt;/dict&gt;
+                &lt;/dict&gt;
+      &lt;key&gt;MaxClients&lt;/key&gt;
+      &lt;integer&gt;5&lt;/integer&gt;
+      &lt;key&gt;memcached&lt;/key&gt;
+      &lt;string&gt;../memcached/_root/bin/memcached&lt;/string&gt; &lt;!-- Find in PATH --&gt;
+      &lt;key&gt;Options&lt;/key&gt;
+      &lt;array&gt;
+        &lt;!--&lt;string&gt;-vv&lt;/string&gt;--&gt;
+      &lt;/array&gt;
+    &lt;/dict&gt;
+
+  &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfcaldavdtestpodBplistfromrev11901CalendarServertrunkconfcaldavdtestpodBplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podB.plist (from rev 11901, CalendarServer/trunk/conf/caldavd-test-podB.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podB.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/caldavd-test-podB.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,158 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+  &lt;dict&gt;
+
+    &lt;!-- Import a parent config before this one --&gt;
+    &lt;key&gt;ImportConfig&lt;/key&gt;
+    &lt;string&gt;./conf/caldavd-test.plist&lt;/string&gt;
+
+    &lt;!-- HTTP port [0 = disable HTTP] --&gt;
+    &lt;key&gt;HTTPPort&lt;/key&gt;
+    &lt;integer&gt;8108&lt;/integer&gt;
+
+    &lt;!-- SSL port [0 = disable HTTPS] --&gt;
+    &lt;!-- (Must also configure SSLCertificate and SSLPrivateKey below) --&gt;
+    &lt;key&gt;SSLPort&lt;/key&gt;
+    &lt;integer&gt;8543&lt;/integer&gt;
+
+    &lt;!-- List of port numbers to bind to for HTTP [empty = same as &quot;Port&quot;] --&gt;
+    &lt;key&gt;BindHTTPPorts&lt;/key&gt;
+    &lt;array&gt;
+    &lt;/array&gt;
+
+    &lt;!-- List of port numbers to bind to for SSL [empty = same as &quot;SSLPort&quot;] --&gt;
+    &lt;key&gt;BindSSLPorts&lt;/key&gt;
+    &lt;array&gt;
+    &lt;/array&gt;
+
+    &lt;!-- Server root --&gt;
+    &lt;key&gt;ServerRoot&lt;/key&gt;
+    &lt;string&gt;./data/podB&lt;/string&gt;
+
+    &lt;!-- Configuration root --&gt;
+    &lt;key&gt;ConfigRoot&lt;/key&gt;
+    &lt;string&gt;./conf&lt;/string&gt;
+
+    &lt;!-- XML File Directory Service --&gt;
+    &lt;key&gt;DirectoryService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFile&lt;/key&gt;
+        &lt;string&gt;./conf/auth/accounts-test-pod.xml&lt;/string&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+    
+    &lt;!-- Resource and Location Service --&gt;
+    &lt;key&gt;ResourceService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;Enabled&lt;/key&gt;
+      &lt;true/&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFile&lt;/key&gt;
+        &lt;string&gt;./conf/auth/resources-test-pod.xml&lt;/string&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+
+    &lt;!-- XML File Augment Service --&gt;
+    &lt;key&gt;AugmentService&lt;/key&gt;
+    &lt;dict&gt;
+      &lt;key&gt;type&lt;/key&gt;
+      &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
+      
+      &lt;key&gt;params&lt;/key&gt;
+      &lt;dict&gt;
+        &lt;key&gt;xmlFiles&lt;/key&gt;
+        &lt;array&gt;
+              &lt;string&gt;./conf/auth/augments-test-pod.xml&lt;/string&gt;
+        &lt;/array&gt;
+      &lt;/dict&gt;
+    &lt;/dict&gt;
+
+    &lt;key&gt;ProxyLoadFromFile&lt;/key&gt;
+    &lt;string&gt;./conf/auth/proxies-test-pod.xml&lt;/string&gt;
+
+    &lt;!--  Servers --&gt;
+    &lt;key&gt;Servers&lt;/key&gt;
+    &lt;dict&gt;
+            &lt;key&gt;Enabled&lt;/key&gt;
+            &lt;true/&gt;
+            &lt;key&gt;ConfigFile&lt;/key&gt;
+            &lt;string&gt;./conf/localservers-test.xml&lt;/string&gt;
+            &lt;key&gt;MaxClients&lt;/key&gt;
+            &lt;integer&gt;5&lt;/integer&gt;
+            &lt;key&gt;InboxName&lt;/key&gt;
+            &lt;string&gt;podding&lt;/string&gt;
+        &lt;/dict&gt;
+
+    &lt;!-- Support for Memcached --&gt;
+    &lt;key&gt;Memcached&lt;/key&gt;
+    &lt;dict&gt;
+          &lt;key&gt;Pools&lt;/key&gt;
+                &lt;dict&gt;
+                  &lt;key&gt;Default&lt;/key&gt;
+                  &lt;dict&gt;
+                    &lt;key&gt;ClientEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;ServerEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;BindAddress&lt;/key&gt;
+                    &lt;string&gt;localhost&lt;/string&gt;
+                    &lt;key&gt;Port&lt;/key&gt;
+                    &lt;integer&gt;11411&lt;/integer&gt;
+                  &lt;/dict&gt;
+                  &lt;key&gt;ProxyDB&lt;/key&gt;
+                  &lt;dict&gt;
+                    &lt;key&gt;ClientEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;ServerEnabled&lt;/key&gt;
+                    &lt;true/&gt;
+                    &lt;key&gt;BindAddress&lt;/key&gt;
+                    &lt;string&gt;localhost&lt;/string&gt;
+                    &lt;key&gt;Port&lt;/key&gt;
+                    &lt;integer&gt;11311&lt;/integer&gt;
+                    &lt;key&gt;HandleCacheTypes&lt;/key&gt;
+                    &lt;array&gt;
+                      &lt;string&gt;ProxyDB&lt;/string&gt;
+                      &lt;string&gt;PrincipalToken&lt;/string&gt;
+                      &lt;string&gt;DIGESTCREDENTIALS&lt;/string&gt;
+                    &lt;/array&gt;
+                  &lt;/dict&gt;
+                &lt;/dict&gt;
+      &lt;key&gt;MaxClients&lt;/key&gt;
+      &lt;integer&gt;5&lt;/integer&gt;
+      &lt;key&gt;memcached&lt;/key&gt;
+      &lt;string&gt;../memcached/_root/bin/memcached&lt;/string&gt; &lt;!-- Find in PATH --&gt;
+      &lt;key&gt;Options&lt;/key&gt;
+      &lt;array&gt;
+        &lt;!--&lt;string&gt;-vv&lt;/string&gt;--&gt;
+      &lt;/array&gt;
+    &lt;/dict&gt;
+
+  &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconflocalserverstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/localservers-test.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/localservers-test.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/localservers-test.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -16,21 +16,19 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx">  --&gt;
</span><span class="cx"> 
</span><del>-&lt;!DOCTYPE servers SYSTEM &quot;servers.dtd&quot;&gt;
</del><ins>+&lt;!DOCTYPE servers SYSTEM &quot;localservers.dtd&quot;&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;servers&gt;
</span><span class="cx">   &lt;server&gt;
</span><del>-    &lt;id&gt;00001&lt;/id&gt;
</del><ins>+    &lt;id&gt;A&lt;/id&gt;
</ins><span class="cx">     &lt;uri&gt;http://localhost:8008&lt;/uri&gt;
</span><del>-    &lt;partitions&gt;
-            &lt;partition&gt;
-                    &lt;id&gt;00001&lt;/id&gt;
-                    &lt;uri&gt;http://localhost:8008&lt;/uri&gt;
-            &lt;/partition&gt;
-            &lt;partition&gt;
-                    &lt;id&gt;00002&lt;/id&gt;
-                    &lt;uri&gt;http://localhost:8108&lt;/uri&gt;
-            &lt;/partition&gt;
-    &lt;/partitions&gt;
</del><ins>+    &lt;allowed-from&gt;localhost&lt;/allowed-from&gt;
+    &lt;shared-secret&gt;A&lt;/shared-secret&gt;
</ins><span class="cx">   &lt;/server&gt;
</span><ins>+  &lt;server&gt;
+    &lt;id&gt;B&lt;/id&gt;
+    &lt;uri&gt;http://localhost:8108&lt;/uri&gt;
+    &lt;allowed-from&gt;localhost&lt;/allowed-from&gt;
+    &lt;shared-secret&gt;B&lt;/shared-secret&gt;
+  &lt;/server&gt;
</ins><span class="cx"> &lt;/servers&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconflocalserversdtdfromrev11901CalendarServertrunkconflocalserversdtd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/localservers.dtd (from rev 11901, CalendarServer/trunk/conf/localservers.dtd) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/localservers.dtd                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/localservers.dtd        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+&lt;!--
+Copyright (c) 2011-2013 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+--&gt;
+
+&lt;!ELEMENT servers (server*) &gt;
+
+        &lt;!ELEMENT server (id, uri, allowed-from*, shared-secret?) &gt;
+                &lt;!ATTLIST server implicit (yes|no) &quot;yes&quot;&gt;
+
+                &lt;!ELEMENT id  (#PCDATA) &gt;
+                &lt;!ELEMENT uri (#PCDATA) &gt;
+                &lt;!ELEMENT allowed-from (#PCDATA) &gt;
+                &lt;!ELEMENT shared-secret (#PCDATA) &gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconflocalserversxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/localservers.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/localservers.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/localservers.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx">  --&gt;
</span><span class="cx"> 
</span><del>-&lt;!DOCTYPE servers SYSTEM &quot;servers.dtd&quot;&gt;
</del><ins>+&lt;!DOCTYPE servers SYSTEM &quot;localservers.dtd&quot;&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;servers&gt;
</span><span class="cx">   &lt;!--
</span><span class="lines">@@ -30,16 +30,6 @@
</span><span class="cx">     &lt;allowed-from&gt;127.0.0.1&lt;/allowed-from&gt;
</span><span class="cx">     &lt;allowed-from&gt;example.local&lt;/allowed-from&gt;
</span><span class="cx">     &lt;shared-secret&gt;ABC&lt;/shared-secret&gt;
</span><del>-    &lt;partitions&gt;
-            &lt;partition&gt;
-                    &lt;id&gt;00001&lt;/id&gt;
-                    &lt;url&gt;https://machine1.example.com:8443&lt;/url&gt;
-            &lt;/partition&gt;
-            &lt;partition&gt;
-                    &lt;id&gt;00002&lt;/id&gt;
-                    &lt;url&gt;https://machine2.example.com:8443&lt;/url&gt;
-            &lt;/partition&gt;
-    &lt;/partitions&gt;
</del><span class="cx">   &lt;/server&gt;
</span><span class="cx">   --&gt;
</span><span class="cx"> &lt;/servers&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfremoteserverstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/remoteservers-test.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/remoteservers-test.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/remoteservers-test.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx">  --&gt;
</span><span class="cx"> 
</span><del>-&lt;!DOCTYPE servers SYSTEM &quot;servertoserver.dtd&quot;&gt;
</del><ins>+&lt;!DOCTYPE servers SYSTEM &quot;remoteservers.dtd&quot;&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;servers&gt;
</span><span class="cx">   &lt;server&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfremoteserversdtdfromrev11901CalendarServertrunkconfremoteserversdtd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/conf/remoteservers.dtd (from rev 11901, CalendarServer/trunk/conf/remoteservers.dtd) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/remoteservers.dtd                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/conf/remoteservers.dtd        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+&lt;!--
+Copyright (c) 2006-2013 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+--&gt;
+
+&lt;!ELEMENT servers (server*) &gt;
+
+        &lt;!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) &gt;
+
+                &lt;!ELEMENT uri (#PCDATA) &gt;
+                &lt;!ELEMENT authentication (user, password) &gt;
+                    &lt;!ATTLIST authentication type (basic) &quot;&quot;&gt;
+                    &lt;!ELEMENT user (#PCDATA) &gt;
+                    &lt;!ELEMENT password (#PCDATA) &gt;
+
+                &lt;!ELEMENT allow-requests-from EMPTY &gt;
+                &lt;!ELEMENT allow-requests-to EMPTY &gt;
+                &lt;!ELEMENT domains (domain*) &gt;
+                        &lt;!ELEMENT domain (#PCDATA) &gt;
+                &lt;!ELEMENT hosts (host*) &gt;
+                        &lt;!ELEMENT host (#PCDATA) &gt;
+                        
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfremoteserversxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/remoteservers.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/remoteservers.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/remoteservers.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> limitations under the License.
</span><span class="cx">  --&gt;
</span><span class="cx"> 
</span><del>-&lt;!DOCTYPE servers SYSTEM &quot;servertoserver.dtd&quot;&gt;
</del><ins>+&lt;!DOCTYPE servers SYSTEM &quot;remoteservers.dtd&quot;&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;servers&gt;
</span><span class="cx">   &lt;!--
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfresourcescaldavdresourcesplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/conf/resources/caldavd-resources.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/resources/caldavd-resources.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/resources/caldavd-resources.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -174,7 +174,6 @@
</span><span class="cx">         Augments for the directory service records to add calendar specific attributes.
</span><span class="cx"> 
</span><span class="cx">         A variety of augment services are available for use.
</span><del>-        When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx">       --&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;!-- XML File Augment Service --&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfserversdtd"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/conf/servers.dtd (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/servers.dtd        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/servers.dtd        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,28 +0,0 @@
</span><del>-&lt;!--
-Copyright (c) 2011-2013 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
---&gt;
-
-&lt;!ELEMENT servers (server*) &gt;
-
-        &lt;!ELEMENT server (id, uri, allowed-from*, shared-secret?, partitions?) &gt;
-                &lt;!ATTLIST server implicit (yes|no) &quot;yes&quot;&gt;
-
-                &lt;!ELEMENT id  (#PCDATA) &gt;
-                &lt;!ELEMENT uri (#PCDATA) &gt;
-                &lt;!ELEMENT allowed-from (#PCDATA) &gt;
-                &lt;!ELEMENT shared-secret (#PCDATA) &gt;
-
-                &lt;!ELEMENT partitions (partition*) &gt;
-                        &lt;!ELEMENT partition (id, uri) &gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfservertoserverdtd"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/conf/servertoserver.dtd (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/servertoserver.dtd        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/servertoserver.dtd        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,33 +0,0 @@
</span><del>-&lt;!--
-Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
---&gt;
-
-&lt;!ELEMENT servers (server*) &gt;
-
-        &lt;!ELEMENT server (uri, authentication?, allow-requests-from, allow-requests-to, domains?, hosts?) &gt;
-
-                &lt;!ELEMENT uri (#PCDATA) &gt;
-                &lt;!ELEMENT authentication (user, password) &gt;
-                    &lt;!ATTLIST authentication type (basic) &quot;&quot;&gt;
-                    &lt;!ELEMENT user (#PCDATA) &gt;
-                    &lt;!ELEMENT password (#PCDATA) &gt;
-
-                &lt;!ELEMENT allow-requests-from EMPTY &gt;
-                &lt;!ELEMENT allow-requests-to EMPTY &gt;
-                &lt;!ELEMENT domains (domain*) &gt;
-                        &lt;!ELEMENT domain (#PCDATA) &gt;
-                &lt;!ELEMENT hosts (host*) &gt;
-                        &lt;!ELEMENT host (#PCDATA) &gt;
-                        
</del><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonconfsudoersplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/conf/sudoers.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/conf/sudoers.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/conf/sudoers.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,42 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-&lt;dict&gt;
-&lt;key&gt;users&lt;/key&gt;
-&lt;array&gt;
-&lt;!-- Sudo user definitions --&gt;
-&lt;!-- With the exception of username and password none of the following
-     elements are used in the current implementation. --&gt;
-&lt;!--
-  &lt;dict&gt;
-    &lt;key&gt;authorize-as&lt;/key&gt;
-    &lt;dict&gt;
-      &lt;key&gt;allow&lt;/key&gt;
-      &lt;true/&gt;
-      &lt;key&gt;principals&lt;/key&gt;
-      &lt;array&gt;
-        &lt;string&gt;all&lt;/string&gt;
-        &lt;string&gt;/principals/user/wsanchez&lt;/string&gt;
-      &lt;/array&gt;
-    &lt;/dict&gt;
-    &lt;key&gt;authorize-from&lt;/key&gt;
-    &lt;array&gt;
-      &lt;string&gt;127.0.0.1&lt;/string&gt;
-    &lt;/array&gt;
-
-    &lt;key&gt;username&lt;/key&gt;
-    &lt;string&gt;&lt;/string&gt;
-
-    &lt;key&gt;password&lt;/key&gt;
-    &lt;string&gt;&lt;/string&gt;
-  &lt;/dict&gt;
---&gt;
-  &lt;dict&gt;
-    &lt;key&gt;username&lt;/key&gt;
-    &lt;string&gt;superuser&lt;/string&gt;
-    &lt;key&gt;password&lt;/key&gt;
-    &lt;string&gt;superuser&lt;/string&gt;
-  &lt;/dict&gt;
-&lt;/array&gt;
-&lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestclientsplistfromrev11901CalendarServertrunkcontribperformanceloadtestclientsplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/clients.plist (from rev 11901, CalendarServer/trunk/contrib/performance/loadtest/clients.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/clients.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/clients.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,445 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2013 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a OS X client simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the OS_X_10_7 instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+        
+                                        &lt;!-- OS_X_10_7 can poll the calendar home at some interval. This is
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;30&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications
+                                                about calendar home changes instead of polling for them periodically. If
+                                                this option is true, then look for the server advertisement for xmpp push
+                                                and use it if possible. Still fall back to polling if there is no xmpp push
+                                                advertised. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;true/&gt;
+                                        &lt;key&gt;ampPushHost&lt;/key&gt;
+                                        &lt;string&gt;localhost&lt;/string&gt;
+                                        &lt;key&gt;ampPushPort&lt;/key&gt;
+                                        &lt;integer&gt;62311&lt;/integer&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;60&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
+                                                                        &lt;key&gt;mu&lt;/key&gt;
+                                                                        &lt;integer&gt;60&lt;/integer&gt;
+
+                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
+                                                                        &lt;key&gt;sigma&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; 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.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;1&lt;/integer&gt;
+                                                                        &lt;!-- mean - average--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;6&lt;/integer&gt;
+                                                                        &lt;!-- maximum --&gt;
+                                                                        &lt;key&gt;maximum&lt;/key&gt;
+                                                                        &lt;real&gt;60&lt;/real&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                                        &lt;!-- median - 50% done--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;1800&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestconfigdistplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.dist.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.dist.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.dist.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -50,10 +50,19 @@
</span><span class="cx">                         &lt;integer&gt;8080&lt;/integer&gt;
</span><span class="cx">                 &lt;/dict&gt;
</span><span class="cx"> 
</span><del>-                &lt;!--  Define whether client data should be saved and re-used. --&gt;
</del><ins>+                &lt;!--  Define whether server supports stats socket. --&gt;
+                &lt;key&gt;serverStats&lt;/key&gt;
+                &lt;dict&gt;
+                        &lt;key&gt;enabled&lt;/key&gt;
+                        &lt;true/&gt;
+                        &lt;key&gt;Port&lt;/key&gt;
+                        &lt;integer&gt;8100&lt;/integer&gt;
+                &lt;/dict&gt;
+
+                &lt;!--  Define whether client data should be re-used. It will always be saved to the specified path.--&gt;
</ins><span class="cx">                 &lt;key&gt;clientDataSerialization&lt;/key&gt;
</span><span class="cx">                 &lt;dict&gt;
</span><del>-                        &lt;key&gt;Enabled&lt;/key&gt;
</del><ins>+                        &lt;key&gt;UseOldData&lt;/key&gt;
</ins><span class="cx">                         &lt;true/&gt;
</span><span class="cx">                         &lt;key&gt;Path&lt;/key&gt;
</span><span class="cx">                         &lt;string&gt;/tmp/sim&lt;/string&gt;
</span><span class="lines">@@ -119,471 +128,6 @@
</span><span class="cx"> 
</span><span class="cx">                 &lt;/dict&gt;
</span><span class="cx"> 
</span><del>-                &lt;!-- Define the kinds of software and user behavior the load simulation 
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations 
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a OS X client simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the OS_X_10_7 instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-
-                                        &lt;!-- OS_X_10_7 can poll the calendar home at some interval. This is 
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;30&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications 
-                                                about calendar home changes instead of polling for them periodically. If 
-                                                this option is true, then look for the server advertisement for xmpp push 
-                                                and use it if possible. Still fall back to polling if there is no xmpp push 
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the 
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create 
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites new attendees to existing events. 
-                                             This profile should no longer be used - use RealisticInviter instead. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Inviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
-                                                                        &lt;key&gt;mu&lt;/key&gt;
-                                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
-                                                                        &lt;key&gt;sigma&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define the distribution of who will be invited to an event. Each 
-                                                                set of credentials loaded by the load tester has an index; samples from this 
-                                                                distribution will be added to that index to arrive at the index of some other 
-                                                                credentials, which will be the target of the invitation. --&gt;
-                                                        &lt;key&gt;inviteeDistanceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;-100&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;101&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
-                                                                        &lt;key&gt;mu&lt;/key&gt;
-                                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
-                                                                        &lt;key&gt;sigma&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;-100&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;101&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;1&lt;/integer&gt;
-                                                                        &lt;!-- mean - average--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;6&lt;/integer&gt;
-                                                                        &lt;!-- maximum --&gt;
-                                                                        &lt;key&gt;maximum&lt;/key&gt;
-                                                                        &lt;real&gt;100&lt;/real&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create 
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will 
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-
</del><span class="cx">                 &lt;!-- Define some log observers to report on the load test. --&gt;
</span><span class="cx">                 &lt;key&gt;observers&lt;/key&gt;
</span><span class="cx">                 &lt;array&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestconfigplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/config.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -37,10 +37,19 @@
</span><span class="cx">                         &lt;integer&gt;8080&lt;/integer&gt;
</span><span class="cx">                 &lt;/dict&gt;
</span><span class="cx"> 
</span><del>-                &lt;!--  Define whether client data should be saved and re-used. --&gt;
</del><ins>+                &lt;!--  Define whether server supports stats socket. --&gt;
+                &lt;key&gt;serverStats&lt;/key&gt;
+                &lt;dict&gt;
+                        &lt;key&gt;enabled&lt;/key&gt;
+                        &lt;true/&gt;
+                        &lt;key&gt;Port&lt;/key&gt;
+                        &lt;integer&gt;8100&lt;/integer&gt;
+                &lt;/dict&gt;
+
+                &lt;!--  Define whether client data should be re-used. It will always be saved to the specified path.--&gt;
</ins><span class="cx">                 &lt;key&gt;clientDataSerialization&lt;/key&gt;
</span><span class="cx">                 &lt;dict&gt;
</span><del>-                        &lt;key&gt;Enabled&lt;/key&gt;
</del><ins>+                        &lt;key&gt;UseOldData&lt;/key&gt;
</ins><span class="cx">                         &lt;true/&gt;
</span><span class="cx">                         &lt;key&gt;Path&lt;/key&gt;
</span><span class="cx">                         &lt;string&gt;/tmp/sim&lt;/string&gt;
</span><span class="lines">@@ -106,429 +115,6 @@
</span><span class="cx"> 
</span><span class="cx">                 &lt;/dict&gt;
</span><span class="cx"> 
</span><del>-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a OS X client simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the OS_X_10_7 instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-        
-                                        &lt;!-- OS_X_10_7 can poll the calendar home at some interval. This is
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;30&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications
-                                                about calendar home changes instead of polling for them periodically. If
-                                                this option is true, then look for the server advertisement for xmpp push
-                                                and use it if possible. Still fall back to polling if there is no xmpp push
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;true/&gt;
-                                        &lt;key&gt;ampPushHost&lt;/key&gt;
-                                        &lt;string&gt;localhost&lt;/string&gt;
-                                        &lt;key&gt;ampPushPort&lt;/key&gt;
-                                        &lt;integer&gt;62311&lt;/integer&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
-                                                                        &lt;key&gt;mu&lt;/key&gt;
-                                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
-                                                                        &lt;key&gt;sigma&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;1&lt;/integer&gt;
-                                                                        &lt;!-- mean - average--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;6&lt;/integer&gt;
-                                                                        &lt;!-- maximum --&gt;
-                                                                        &lt;key&gt;maximum&lt;/key&gt;
-                                                                        &lt;real&gt;60&lt;/real&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-
</del><span class="cx">                 &lt;!-- Define some log observers to report on the load test. --&gt;
</span><span class="cx">                 &lt;key&gt;observers&lt;/key&gt;
</span><span class="cx">                 &lt;array&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestpopulationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/population.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/population.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/population.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -396,6 +396,7 @@
</span><span class="cx">         self._failed_clients = []
</span><span class="cx">         self._failed_sim = collections.defaultdict(int)
</span><span class="cx">         self._startTime = datetime.now()
</span><ins>+        self._expired_data = None
</ins><span class="cx"> 
</span><span class="cx">         # Load parameters from config
</span><span class="cx">         if &quot;thresholdsPath&quot; in params:
</span><span class="lines">@@ -423,6 +424,13 @@
</span><span class="cx">             self._fail_cut_off = params[&quot;failCutoff&quot;]
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def observe(self, event):
+        if event.get('type') == 'sim-expired':
+            self.simExpired(event)
+        else:
+            super(ReportStatistics, self).observe(event)
+
+
</ins><span class="cx">     def countUsers(self):
</span><span class="cx">         return len(self._users)
</span><span class="cx"> 
</span><span class="lines">@@ -454,6 +462,10 @@
</span><span class="cx">         self._failed_sim[event['reason']] += 1
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def simExpired(self, event):
+        self._expired_data = event['reason']
+
+
</ins><span class="cx">     def printMiscellaneous(self, output, items):
</span><span class="cx">         maxColumnWidth = str(len(max(items.iterkeys(), key=len)))
</span><span class="cx">         fmt = &quot;%&quot; + maxColumnWidth + &quot;s : %-s\n&quot;
</span><span class="lines">@@ -480,7 +492,7 @@
</span><span class="cx">             if result is not None:
</span><span class="cx">                 differences.append(result)
</span><span class="cx"> 
</span><del>-        return mean(differences) if differences else &quot;None&quot;
</del><ins>+        return (&quot;%-8.4f&quot; % mean(differences)) if differences else &quot;None&quot;
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def qos_value(self, method, value):
</span><span class="lines">@@ -518,7 +530,7 @@
</span><span class="cx">             'Start time': self._startTime.strftime('%m/%d %H:%M:%S'),
</span><span class="cx">             'Run time': &quot;%02d:%02d:%02d&quot; % (runHours, runMinutes, runSeconds),
</span><span class="cx">             'CPU Time': &quot;user %-5.2f sys %-5.2f total %02d:%02d:%02d&quot; % (cpuUser, cpuSys, cpuHours, cpuMinutes, cpuSeconds,),
</span><del>-            'QoS': &quot;%-8.4f&quot; % (self.qos(),),
</del><ins>+            'QoS': self.qos(),
</ins><span class="cx">         }
</span><span class="cx">         if self.countClientFailures() &gt; 0:
</span><span class="cx">             items['Failed clients'] = self.countClientFailures()
</span><span class="lines">@@ -527,8 +539,22 @@
</span><span class="cx">         if self.countSimFailures() &gt; 0:
</span><span class="cx">             for reason, count in self._failed_sim.items():
</span><span class="cx">                 items['Failed operation'] = &quot;%s : %d times&quot; % (reason, count,)
</span><ins>+        output.write(&quot;* Client\n&quot;)
</ins><span class="cx">         self.printMiscellaneous(output, items)
</span><span class="cx">         output.write(&quot;\n&quot;)
</span><ins>+
+        if self._expired_data is not None:
+            items = {
+                &quot;Req/sec&quot; : &quot;%.1f&quot; % (self._expired_data[0],),
+                &quot;Response&quot;: &quot;%.1f (ms)&quot; % (self._expired_data[1],),
+                &quot;Slots&quot;: &quot;%.2f&quot; % (self._expired_data[2],),
+                &quot;CPU&quot;: &quot;%.1f%%&quot; % (self._expired_data[3],),
+            }
+            output.write(&quot;* Server (Last 5 minutes)\n&quot;)
+            self.printMiscellaneous(output, items)
+            output.write(&quot;\n&quot;)
+        output.write(&quot;* Details\n&quot;)
+
</ins><span class="cx">         self.printHeader(output, [
</span><span class="cx">                 (label, width)
</span><span class="cx">                 for (label, width, _ignore_fmt)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtestsimpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/sim.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/sim.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/sim.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -23,11 +23,15 @@
</span><span class="cx"> from plistlib import readPlist
</span><span class="cx"> from random import Random
</span><span class="cx"> from sys import argv, stdout
</span><ins>+from urlparse import urlsplit
</ins><span class="cx"> from xml.parsers.expat import ExpatError
</span><ins>+import json
+import shutil
+import socket
</ins><span class="cx"> 
</span><span class="cx"> from twisted.python import context
</span><span class="cx"> from twisted.python.filepath import FilePath
</span><del>-from twisted.python.log import startLogging, addObserver, removeObserver
</del><ins>+from twisted.python.log import startLogging, addObserver, removeObserver, msg
</ins><span class="cx"> from twisted.python.usage import UsageError, Options
</span><span class="cx"> from twisted.python.reflect import namedAny
</span><span class="cx"> 
</span><span class="lines">@@ -56,6 +60,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def safeDivision(value, total, factor=1):
+    return value * factor / total if total else 0
+
+
+
</ins><span class="cx"> def generateRecords(count, uidPattern=&quot;user%d&quot;, passwordPattern=&quot;user%d&quot;,
</span><span class="cx">     namePattern=&quot;User %d&quot;, emailPattern=&quot;user%d@example.com&quot;):
</span><span class="cx">     for i in xrange(count):
</span><span class="lines">@@ -121,6 +130,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     config = None
</span><span class="cx">     _defaultConfig = FilePath(__file__).sibling(&quot;config.plist&quot;)
</span><ins>+    _defaultClients = FilePath(__file__).sibling(&quot;clients.plist&quot;)
</ins><span class="cx"> 
</span><span class="cx">     optParameters = [
</span><span class="cx">         (&quot;runtime&quot;, &quot;t&quot;, None,
</span><span class="lines">@@ -129,6 +139,9 @@
</span><span class="cx">         (&quot;config&quot;, None, _defaultConfig,
</span><span class="cx">          &quot;Configuration plist file name from which to read simulation parameters.&quot;,
</span><span class="cx">          FilePath),
</span><ins>+        (&quot;clients&quot;, None, _defaultClients,
+         &quot;Configuration plist file name from which to read client parameters.&quot;,
+         FilePath),
</ins><span class="cx">         ]
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -181,7 +194,23 @@
</span><span class="cx">         finally:
</span><span class="cx">             configFile.close()
</span><span class="cx"> 
</span><ins>+        try:
+            clientFile = self['clients'].open()
+        except IOError, e:
+            raise UsageError(&quot;--clients %s: %s&quot; % (
+                    self['clients'].path, e.strerror))
+        try:
+            try:
+                client_config = readPlist(clientFile)
+                self.config[&quot;clients&quot;] = client_config[&quot;clients&quot;]
+                if &quot;arrivalInterval&quot; in client_config:
+                    self.config[&quot;arrival&quot;][&quot;params&quot;][&quot;interval&quot;] = client_config[&quot;arrivalInterval&quot;]
+            except ExpatError, e:
+                raise UsageError(&quot;--clients %s: %s&quot; % (self['clients'].path, e))
+        finally:
+            clientFile.close()
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> Arrival = namedtuple('Arrival', 'factory parameters')
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -200,7 +229,7 @@
</span><span class="cx">         user information about the accounts on the server being put
</span><span class="cx">         under load.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    def __init__(self, server, principalPathTemplate, webadminPort, serializationPath, arrival, parameters, observers=None,
</del><ins>+    def __init__(self, server, principalPathTemplate, webadminPort, serverStats, serializationPath, arrival, parameters, observers=None,
</ins><span class="cx">                  records=None, reactor=None, runtime=None, workers=None,
</span><span class="cx">                  configTemplate=None, workerID=None, workerCount=1):
</span><span class="cx">         if reactor is None:
</span><span class="lines">@@ -208,6 +237,7 @@
</span><span class="cx">         self.server = server
</span><span class="cx">         self.principalPathTemplate = principalPathTemplate
</span><span class="cx">         self.webadminPort = webadminPort
</span><ins>+        self.serverStats = serverStats
</ins><span class="cx">         self.serializationPath = serializationPath
</span><span class="cx">         self.arrival = arrival
</span><span class="cx">         self.parameters = parameters
</span><span class="lines">@@ -260,15 +290,17 @@
</span><span class="cx">                 principalPathTemplate = config['principalPathTemplate']
</span><span class="cx"> 
</span><span class="cx">             if 'clientDataSerialization' in config:
</span><del>-                if config['clientDataSerialization']['Enabled']:
-                    serializationPath = config['clientDataSerialization']['Path']
-                    if not isdir(serializationPath):
-                        try:
-                            mkdir(serializationPath)
-                        except OSError:
-                            print(&quot;Unable to create client data serialization directory: %s&quot; % (serializationPath))
-                            print(&quot;Please consult the clientDataSerialization stanza of contrib/performance/loadtest/config.plist&quot;)
-                            raise
</del><ins>+                serializationPath = config['clientDataSerialization']['Path']
+                if not config['clientDataSerialization']['UseOldData']:
+                    shutil.rmtree(serializationPath)
+                serializationPath = config['clientDataSerialization']['Path']
+                if not isdir(serializationPath):
+                    try:
+                        mkdir(serializationPath)
+                    except OSError:
+                        print(&quot;Unable to create client data serialization directory: %s&quot; % (serializationPath))
+                        print(&quot;Please consult the clientDataSerialization stanza of contrib/performance/loadtest/config.plist&quot;)
+                        raise
</ins><span class="cx"> 
</span><span class="cx">             if 'arrival' in config:
</span><span class="cx">                 arrival = Arrival(
</span><span class="lines">@@ -310,6 +342,12 @@
</span><span class="cx">             if config['webadmin']['enabled']:
</span><span class="cx">                 webadminPort = config['webadmin']['HTTPPort']
</span><span class="cx"> 
</span><ins>+        serverStats = None
+        if 'serverStats' in config:
+            if config['serverStats']['enabled']:
+                serverStats = config['serverStats']
+                serverStats['server'] = config['server'] if 'server' in config else ''
+
</ins><span class="cx">         observers = []
</span><span class="cx">         if 'observers' in config:
</span><span class="cx">             for observer in config['observers']:
</span><span class="lines">@@ -324,11 +362,23 @@
</span><span class="cx">             records.extend(namedAny(loader)(**params))
</span><span class="cx">             output.write(&quot;Loaded {0} accounts.\n&quot;.format(len(records)))
</span><span class="cx"> 
</span><del>-        return cls(server, principalPathTemplate, webadminPort, serializationPath,
-                   arrival, parameters, observers=observers,
-                   records=records, runtime=runtime, reactor=reactor,
-                   workers=workers, configTemplate=configTemplate,
-                   workerID=workerID, workerCount=workerCount)
</del><ins>+        return cls(
+            server,
+            principalPathTemplate,
+            webadminPort,
+            serverStats,
+            serializationPath,
+            arrival,
+            parameters,
+            observers=observers,
+            records=records,
+            runtime=runtime,
+            reactor=reactor,
+            workers=workers,
+            configTemplate=configTemplate,
+            workerID=workerID,
+            workerCount=workerCount,
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classmethod
</span><span class="lines">@@ -409,7 +459,7 @@
</span><span class="cx">     def run(self, output=stdout):
</span><span class="cx">         self.attachServices(output)
</span><span class="cx">         if self.runtime is not None:
</span><del>-            self.reactor.callLater(self.runtime, self.reactor.stop)
</del><ins>+            self.reactor.callLater(self.runtime, self.stopAndReport)
</ins><span class="cx">         if self.webadminPort:
</span><span class="cx">             self.reactor.listenTCP(self.webadminPort, server.Site(LoadSimAdminResource(self)))
</span><span class="cx">         self.reactor.run()
</span><span class="lines">@@ -417,16 +467,65 @@
</span><span class="cx"> 
</span><span class="cx">     def stop(self):
</span><span class="cx">         if self.ms.running:
</span><ins>+            self.updateStats()
</ins><span class="cx">             self.ms.stopService()
</span><del>-            self.reactor.callLater(5, self.reactor.stop)
</del><ins>+            self.reactor.callLater(5, self.stopAndReport)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def shutdown(self):
</span><span class="cx">         if self.ms.running:
</span><ins>+            self.updateStats()
</ins><span class="cx">             return self.ms.stopService()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def updateStats(self):
+        &quot;&quot;&quot;
+        Capture server stats and stop.
+        &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+        if self.serverStats is not None:
+            _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.serverStats[&quot;server&quot;])
+            data = self.readStatsSock((hostname.split(&quot;:&quot;)[0], self.serverStats[&quot;Port&quot;],), True)
+            if &quot;Failed&quot; not in data:
+                data = data[&quot;5 Minutes&quot;]
+                result = (
+                    safeDivision(float(data[&quot;requests&quot;]), 5 * 60),
+                    safeDivision(data[&quot;t&quot;], data[&quot;requests&quot;]),
+                    safeDivision(float(data[&quot;slots&quot;]), data[&quot;requests&quot;]),
+                    safeDivision(data[&quot;cpu&quot;], data[&quot;requests&quot;]),
+                )
+                msg(type=&quot;sim-expired&quot;, reason=result)
+
+
+    def stopAndReport(self):
+        &quot;&quot;&quot;
+        Runtime has expired - capture server stats and stop.
+        &quot;&quot;&quot;
+
+        self.updateStats()
+        self.reactor.stop()
+
+
+    def readStatsSock(self, sockname, useTCP):
+        try:
+            s = socket.socket(socket.AF_INET if useTCP else socket.AF_UNIX, socket.SOCK_STREAM)
+            s.connect(sockname)
+            data = &quot;&quot;
+            while True:
+                d = s.recv(1024)
+                if d:
+                    data += d
+                else:
+                    break
+            s.close()
+            data = json.loads(data)
+        except socket.error:
+            data = {&quot;Failed&quot;: &quot;Unable to read statistics from server: %s&quot; % (sockname,)}
+        data[&quot;Server&quot;] = sockname
+        return data
+
+
+
</ins><span class="cx"> def attachService(reactor, loadsim, service):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Attach a given L{IService} provider to the given L{IReactorCore}; cause it
</span><span class="lines">@@ -557,7 +656,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def errReceived(self, error):
</span><del>-        from twisted.python.log import msg
</del><span class="cx">         msg(&quot;stderr received from &quot; + str(self.transport.pid))
</span><span class="cx">         msg(&quot;    &quot; + repr(error))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigseventsonlyplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist (11901 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,440 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a Lion iCal simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-
-                                        &lt;!-- Client can poll the calendar home at some interval. This is 
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;300000&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
-                                                about calendar home changes instead of polling for them periodically. If 
-                                                this option is true, then look for the server advertisement for xmpp push 
-                                                and use it if possible. Still fall back to polling if there is no xmpp push 
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the 
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create 
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;20&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
-                                                                        &lt;key&gt;mu&lt;/key&gt;
-                                                                        &lt;integer&gt;10&lt;/integer&gt;
-
-                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
-                                                                        &lt;key&gt;sigma&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;1&lt;/integer&gt;
-                                                                        &lt;!-- mean - average--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;6&lt;/integer&gt;
-                                                                        &lt;!-- maximum --&gt;
-                                                                        &lt;key&gt;maximum&lt;/key&gt;
-                                                                        &lt;real&gt;100&lt;/real&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create 
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will 
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigseventsonlyplistfromrev11901CalendarServertrunkcontribperformanceloadteststandardconfigseventsonlyplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist (from rev 11901, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/events-only.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,440 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a Lion iCal simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+
+                                        &lt;!-- Client can poll the calendar home at some interval. This is 
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;300000&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
+                                                about calendar home changes instead of polling for them periodically. If 
+                                                this option is true, then look for the server advertisement for xmpp push 
+                                                and use it if possible. Still fall back to polling if there is no xmpp push 
+                                                advertised. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the 
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create 
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;20&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
+                                                                        &lt;key&gt;mu&lt;/key&gt;
+                                                                        &lt;integer&gt;10&lt;/integer&gt;
+
+                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
+                                                                        &lt;key&gt;sigma&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; 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.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;1&lt;/integer&gt;
+                                                                        &lt;!-- mean - average--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;6&lt;/integer&gt;
+                                                                        &lt;!-- maximum --&gt;
+                                                                        &lt;key&gt;maximum&lt;/key&gt;
+                                                                        &lt;real&gt;100&lt;/real&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                                        &lt;!-- median - 50% done--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;1800&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create 
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will 
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesacceptsplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist (11901 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,419 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a Lion iCal simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-
-                                        &lt;!-- Client can poll the calendar home at some interval. This is 
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;300000&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
-                                                about calendar home changes instead of polling for them periodically. If 
-                                                this option is true, then look for the server advertisement for xmpp push 
-                                                and use it if possible. Still fall back to polling if there is no xmpp push 
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;true /&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the 
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create 
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;20&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- interval (in seconds). --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;150&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- Number of attendees. --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;100&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformDiscreteDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- Set of values to use - will be chosen in random order. --&gt;
-                                                                        &lt;key&gt;values&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;integer&gt;15&lt;/integer&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                &lt;integer&gt;25&lt;/integer&gt;
-                                                                                &lt;integer&gt;30&lt;/integer&gt;
-                                                                        &lt;/array&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create 
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will 
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-
-                &lt;!-- Determine the interval between client creation. --&gt;
-                &lt;key&gt;arrivalInterval&lt;/key&gt;
-                &lt;integer&gt;5&lt;/integer&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesacceptsplistfromrev11901CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesacceptsplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist (from rev 11901, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-accepts.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,419 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a Lion iCal simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+
+                                        &lt;!-- Client can poll the calendar home at some interval. This is 
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;300000&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
+                                                about calendar home changes instead of polling for them periodically. If 
+                                                this option is true, then look for the server advertisement for xmpp push 
+                                                and use it if possible. Still fall back to polling if there is no xmpp push 
+                                                advertised. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;true /&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the 
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create 
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;20&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- interval (in seconds). --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;150&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; 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.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- Number of attendees. --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;100&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformDiscreteDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- Set of values to use - will be chosen in random order. --&gt;
+                                                                        &lt;key&gt;values&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;integer&gt;15&lt;/integer&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                &lt;integer&gt;25&lt;/integer&gt;
+                                                                                &lt;integer&gt;30&lt;/integer&gt;
+                                                                        &lt;/array&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create 
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will 
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+
+                &lt;!-- Determine the interval between client creation. --&gt;
+                &lt;key&gt;arrivalInterval&lt;/key&gt;
+                &lt;integer&gt;5&lt;/integer&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyrecurringplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist (11901 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,414 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a Lion iCal simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-
-                                        &lt;!-- Client can poll the calendar home at some interval. This is 
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;300000&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
-                                                about calendar home changes instead of polling for them periodically. If 
-                                                this option is true, then look for the server advertisement for xmpp push 
-                                                and use it if possible. Still fall back to polling if there is no xmpp push 
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the 
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create 
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;20&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- interval (in seconds). --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;120&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- Number of attendees. --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;100&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create 
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will 
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-
-                &lt;!-- Determine the interval between client creation. --&gt;
-                &lt;key&gt;arrivalInterval&lt;/key&gt;
-                &lt;integer&gt;4&lt;/integer&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyrecurringplistfromrev11901CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesonlyrecurringplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist (from rev 11901, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,414 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a Lion iCal simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+
+                                        &lt;!-- Client can poll the calendar home at some interval. This is 
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;300000&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
+                                                about calendar home changes instead of polling for them periodically. If 
+                                                this option is true, then look for the server advertisement for xmpp push 
+                                                and use it if possible. Still fall back to polling if there is no xmpp push 
+                                                advertised. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the 
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create 
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;20&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- interval (in seconds). --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;120&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; 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.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- Number of attendees. --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;100&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                                        &lt;!-- median - 50% done--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;1800&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create 
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will 
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+
+                &lt;!-- Determine the interval between client creation. --&gt;
+                &lt;key&gt;arrivalInterval&lt;/key&gt;
+                &lt;integer&gt;4&lt;/integer&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist (11901 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,430 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a Lion iCal simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-
-                                        &lt;!-- Client can poll the calendar home at some interval. This is 
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;300000&lt;/integer&gt;
-
-                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
-                                                about calendar home changes instead of polling for them periodically. If 
-                                                this option is true, then look for the server advertisement for xmpp push 
-                                                and use it if possible. Still fall back to polling if there is no xmpp push 
-                                                advertised. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;false /&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the 
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create 
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;20&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- interval (in seconds). --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;120&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; 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.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- Number of attendees. --&gt;
-                                                                        &lt;key&gt;value&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;false/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create 
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
-                                                                        in the near future, limited to certain days of the week and certain hours 
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will 
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadteststandardconfigsinvitesonlyplistfromrev11901CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesonlyplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist (from rev 11901, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only.plist) (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,430 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a Lion iCal simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+
+                                        &lt;!-- Client can poll the calendar home at some interval. This is 
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;300000&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS X 10.6 can wait for notifications 
+                                                about calendar home changes instead of polling for them periodically. If 
+                                                this option is true, then look for the server advertisement for xmpp push 
+                                                and use it if possible. Still fall back to polling if there is no xmpp push 
+                                                advertised. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the 
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create 
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;20&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- interval (in seconds). --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;120&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- 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 &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; 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.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.FixedDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- Number of attendees. --&gt;
+                                                                        &lt;key&gt;value&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;false/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                                        &lt;!-- median - 50% done--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;1800&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create 
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use 
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks 
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value 
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps 
+                                                                        in the near future, limited to certain days of the week and certain hours 
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled 
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this 
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will 
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformanceloadtesttest_simpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/test_sim.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/test_sim.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/loadtest/test_sim.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -253,7 +253,7 @@
</span><span class="cx">         exc = self.assertRaises(
</span><span class="cx">             SystemExit, StubSimulator.main, ['--config', config.path])
</span><span class="cx">         self.assertEquals(
</span><del>-            exc.args, (StubSimulator(None, None, None, None, None, None).run(),))
</del><ins>+            exc.args, (StubSimulator(None, None, None, None, None, None, None).run(),))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_createSimulator(self):
</span><span class="lines">@@ -264,7 +264,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         server = 'http://127.0.0.7:1243/'
</span><span class="cx">         reactor = object()
</span><del>-        sim = LoadSimulator(server, None, None, None, None, None, reactor=reactor)
</del><ins>+        sim = LoadSimulator(server, None, None, None, None, None, None, reactor=reactor)
</ins><span class="cx">         calsim = sim.createSimulator()
</span><span class="cx">         self.assertIsInstance(calsim, CalendarClientSimulator)
</span><span class="cx">         self.assertIsInstance(calsim.reactor, LagTrackingReactor)
</span><span class="lines">@@ -447,7 +447,7 @@
</span><span class="cx"> 
</span><span class="cx">         reactor = object()
</span><span class="cx">         sim = LoadSimulator(
</span><del>-            None, None, None, None, Arrival(FakeArrival, {'x': 3, 'y': 2}), None, reactor=reactor)
</del><ins>+            None, None, None, None, None, Arrival(FakeArrival, {'x': 3, 'y': 2}), None, reactor=reactor)
</ins><span class="cx">         arrival = sim.createArrivalPolicy()
</span><span class="cx">         self.assertIsInstance(arrival, FakeArrival)
</span><span class="cx">         self.assertIdentical(arrival.reactor, sim.reactor)
</span><span class="lines">@@ -478,7 +478,9 @@
</span><span class="cx">                             &quot;weight&quot;: 3,
</span><span class="cx">                             }]}))
</span><span class="cx"> 
</span><del>-        sim = LoadSimulator.fromCommandLine(['--config', config.path])
</del><ins>+        sim = LoadSimulator.fromCommandLine(
+            ['--config', config.path, '--clients', config.path]
+        )
</ins><span class="cx">         expectedParameters = PopulationParameters()
</span><span class="cx">         expectedParameters.addClient(
</span><span class="cx">             3, ClientType(OS_X_10_6, {&quot;foo&quot;: &quot;bar&quot;}, [ProfileType(Eventer, {
</span><span class="lines">@@ -495,7 +497,9 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         config = FilePath(self.mktemp())
</span><span class="cx">         config.setContent(writePlistToString({&quot;clients&quot;: []}))
</span><del>-        sim = LoadSimulator.fromCommandLine(['--config', config.path])
</del><ins>+        sim = LoadSimulator.fromCommandLine(
+            ['--config', config.path, '--clients', config.path]
+        )
</ins><span class="cx">         expectedParameters = PopulationParameters()
</span><span class="cx">         expectedParameters.addClient(
</span><span class="cx">             1, ClientType(OS_X_10_6, {}, [Eventer, Inviter, Accepter]))
</span><span class="lines">@@ -528,6 +532,7 @@
</span><span class="cx">             &quot;/principals/users/%s/&quot;,
</span><span class="cx">             None,
</span><span class="cx">             None,
</span><ins>+            None,
</ins><span class="cx">             Arrival(lambda reactor: NullArrival(), {}),
</span><span class="cx">             None, observers, reactor=Reactor())
</span><span class="cx">         io = StringIO()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformancesqlusagerequestshttpTestspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/requests/httpTests.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/requests/httpTests.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/requests/httpTests.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -89,12 +89,21 @@
</span><span class="cx">             pos = line.find(&quot;: &quot;)
</span><span class="cx">             return float(line[pos + 2:])
</span><span class="cx"> 
</span><ins>+        # Need to skip over stats that are unlabeled
</ins><span class="cx">         data = open(self.logFilePath).read()
</span><span class="cx">         lines = data.splitlines()
</span><del>-        count = extractInt(lines[4])
-        rows = extractInt(lines[5])
-        timing = extractFloat(lines[6])
-        self.result = HTTPTestBase.SQLResults(count, rows, timing)
</del><ins>+        offset = 0
+        while True:
+            if lines[offset] == &quot;*** SQL Stats ***&quot;:
+                if lines[offset + 2].split()[1] != &quot;unlabeled&quot;:
+                    count = extractInt(lines[offset + 4])
+                    rows = extractInt(lines[offset + 5])
+                    timing = extractFloat(lines[offset + 6])
+                    self.result = HTTPTestBase.SQLResults(count, rows, timing)
+                    break
+            offset += 1
+        else:
+            self.result = HTTPTestBase.SQLResults(-1, -1, 0.0)
</ins><span class="cx"> 
</span><span class="cx">         with open(&quot;%s-%d-%s&quot; % (self.logFilePath, event_count, self.label), &quot;w&quot;) as f:
</span><span class="cx">             f.write(data)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribperformancesqlusagesqlusagepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/sqlusage.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/sqlusage.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/performance/sqlusage/sqlusage.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -127,11 +127,17 @@
</span><span class="cx">         ]
</span><span class="cx">         self.requestLabels = [request.label for request in requests]
</span><span class="cx"> 
</span><del>-        # Warm-up server by doing calendar home and calendar propfinds
-        props = (davxml.resourcetype,)
-        for session in sessions:
-            session.getPropertiesOnHierarchy(URL(path=session.homeHref), props)
-            session.getPropertiesOnHierarchy(URL(path=session.calendarHref), props)
</del><ins>+        def _warmUp():
+            # Warm-up server by doing calendar home and child collection propfinds.
+            # Do this twice because the very first time might provision DB objects and
+            # blow any DB cache - the second time will warm the DB cache.
+            props = (davxml.resourcetype,)
+            for _ignore in range(2):
+                for session in sessions:
+                    session.getPropertiesOnHierarchy(URL(path=session.homeHref), props)
+                    session.getPropertiesOnHierarchy(URL(path=session.calendarHref), props)
+                    session.getPropertiesOnHierarchy(URL(path=session.inboxHref), props)
+                    session.getPropertiesOnHierarchy(URL(path=session.notificationHref), props)
</ins><span class="cx"> 
</span><span class="cx">         # Now loop over sets of events
</span><span class="cx">         for count in event_counts:
</span><span class="lines">@@ -140,6 +146,7 @@
</span><span class="cx">             result = {}
</span><span class="cx">             for request in requests:
</span><span class="cx">                 print(&quot;  Test = %s&quot; % (request.label,))
</span><ins>+                _warmUp()
</ins><span class="cx">                 result[request.label] = request.execute(count)
</span><span class="cx">             self.results[count] = result
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribtoolsprotocolanalysispy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/tools/protocolanalysis.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/tools/protocolanalysis.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/tools/protocolanalysis.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -293,6 +293,12 @@
</span><span class="cx">         self.userCounts = collections.defaultdict(int)
</span><span class="cx">         self.userResponseTimes = collections.defaultdict(float)
</span><span class="cx"> 
</span><ins>+        self.newEvents = 0
+        self.newInvites = 0
+        self.updateEvents = 0
+        self.updateInvites = 0
+        self.attendeeInvites = 0
+
</ins><span class="cx">         self.otherUserCalendarRequests = {}
</span><span class="cx"> 
</span><span class="cx">         self.currentLine = None
</span><span class="lines">@@ -416,6 +422,19 @@
</span><span class="cx">                 self.hourlyByStatus[&quot; TOTAL&quot;][timeBucketIndex] += 1
</span><span class="cx">                 self.hourlyByStatus[self.currentLine.status][timeBucketIndex] += 1
</span><span class="cx"> 
</span><ins>+                if self.currentLine.status == 201:
+                    if adjustedMethod == METHOD_PUT_ICS:
+                        self.newEvents += 1
+                    elif adjustedMethod == METHOD_PUT_ORGANIZER:
+                        self.newInvites += 1
+                elif isOK:
+                    if adjustedMethod == METHOD_PUT_ICS:
+                        self.updateEvents += 1
+                    elif adjustedMethod == METHOD_PUT_ORGANIZER:
+                        self.updateInvites += 1
+                    elif adjustedMethod == METHOD_PUT_ATTENDEE:
+                        self.attendeeInvites += 1
+
</ins><span class="cx">                 # Cache analysis
</span><span class="cx">                 if adjustedMethod == METHOD_PROPFIND_CALENDAR and self.currentLine.status == 207:
</span><span class="cx">                     responses = int(self.currentLine.extended.get(&quot;responses&quot;, 0))
</span><span class="lines">@@ -1029,7 +1048,10 @@
</span><span class="cx">             #print(&quot;User Response times&quot;)
</span><span class="cx">             #self.printUserResponseTimes(doTabs)
</span><span class="cx"> 
</span><ins>+            print(&quot;Sim values&quot;)
+            self.printSimStats(doTabs)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def printInfo(self, doTabs):
</span><span class="cx"> 
</span><span class="cx">         table = tables.Table()
</span><span class="lines">@@ -1083,6 +1105,7 @@
</span><span class="cx">         totalRequests = 0
</span><span class="cx">         totalDepth = 0
</span><span class="cx">         totalTime = 0.0
</span><ins>+        self.timeCounts = 0
</ins><span class="cx">         for ctr in xrange(self.timeBucketCount):
</span><span class="cx">             hour = self.getHourFromIndex(ctr)
</span><span class="cx">             if hour is None:
</span><span class="lines">@@ -1101,12 +1124,13 @@
</span><span class="cx">             totalRequests += countRequests
</span><span class="cx">             totalDepth += countDepth
</span><span class="cx">             totalTime += countTime
</span><ins>+            self.timeCounts += 1
</ins><span class="cx"> 
</span><span class="cx">         table.addFooter(
</span><span class="cx">             (
</span><span class="cx">                 &quot;Total:&quot;,
</span><span class="cx">                 totalRequests,
</span><del>-                (1.0 * totalRequests) / self.timeBucketCount / self.resolutionMinutes / 60,
</del><ins>+                safePercent(totalRequests, self.timeCounts * self.resolutionMinutes * 60, 1.0),
</ins><span class="cx">                 safePercent(totalTime, totalRequests, 1.0),
</span><span class="cx">                 safePercent(float(totalDepth), totalRequests, 1),
</span><span class="cx">             ),
</span><span class="lines">@@ -1545,7 +1569,38 @@
</span><span class="cx">         print(&quot;&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def printSimStats(self, doTabs):
+        users = len(self.userCounts.keys())
+        hours = self.timeCounts / self.resolutionMinutes / 60
+        table = tables.Table()
+        table.setDefaultColumnFormats((
+                tables.Table.ColumnFormat(&quot;%s&quot;, tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                tables.Table.ColumnFormat(&quot;%s&quot;, tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                tables.Table.ColumnFormat(&quot;%s&quot;, tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                tables.Table.ColumnFormat(&quot;%s&quot;, tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+                ))
+        table.addHeader((&quot;Item&quot;, &quot;Value&quot;, &quot;Items, per User, per Day&quot;, &quot;Interval (sec), per item, per user&quot;))
+        table.addRow((&quot;Unique Users&quot;, users, &quot;&quot;, &quot;&quot;))
</ins><span class="cx"> 
</span><ins>+        def _addRow(title, item):
+            table.addRow((title, item, &quot;%.1f&quot; % (safePercent(24 * item, hours * users, 1.0),), &quot;%.1f&quot; % (safePercent(hours * 60 * 60 * users, item, 1.0),),))
+
+        _addRow(&quot;New Events&quot;, self.newEvents)
+        _addRow(&quot;New Invites&quot;, self.newInvites)
+        _addRow(&quot;Updated Events&quot;, self.updateEvents)
+        _addRow(&quot;Updated Invites&quot;, self.updateInvites)
+        _addRow(&quot;Attendee Invites&quot;, self.attendeeInvites)
+        table.addRow((
+            &quot;Recipients&quot;,
+            &quot;%.1f&quot; % (safePercent(sum(self.averagedHourlyByRecipientCount[&quot;iTIP Average&quot;]), self.timeCounts, 1.0),),
+            &quot;&quot;,
+            &quot;&quot;,
+        ))
+        table.printTabDelimitedData() if doTabs else table.printTable()
+        print(&quot;&quot;)
+
+
+
</ins><span class="cx"> class TablePrinter(object):
</span><span class="cx"> 
</span><span class="cx">     @classmethod
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsoncontribtoolsrequest_monitorpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/contrib/tools/request_monitor.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/contrib/tools/request_monitor.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/contrib/tools/request_monitor.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -303,8 +303,6 @@
</span><span class="cx">     print(&quot;--procs N  specifies how many python processes are expected in the log file (default: 80)&quot;)
</span><span class="cx">     print(&quot;--top N    how many long requests to print (default: 10)&quot;)
</span><span class="cx">     print(&quot;--users N  how many top users to print (default: 5)&quot;)
</span><del>-    print(&quot;--router   analyze a partition server router node&quot;)
-    print(&quot;--worker   analyze a partition server worker node&quot;)
</del><span class="cx">     print(&quot;&quot;)
</span><span class="cx">     print(&quot;Version: 5&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -313,19 +311,13 @@
</span><span class="cx"> numTop = 10
</span><span class="cx"> numUsers = 5
</span><span class="cx"> lineRange = None
</span><del>-router = False
-worker = False
-options, args = getopt.getopt(sys.argv[1:], &quot;h&quot;, [&quot;debug&quot;, &quot;router&quot;, &quot;worker&quot;, &quot;lines=&quot;, &quot;range=&quot;, &quot;procs=&quot;, &quot;top=&quot;, &quot;users=&quot;])
</del><ins>+options, args = getopt.getopt(sys.argv[1:], &quot;h&quot;, [&quot;debug&quot;, &quot;lines=&quot;, &quot;range=&quot;, &quot;procs=&quot;, &quot;top=&quot;, &quot;users=&quot;])
</ins><span class="cx"> for option, value in options:
</span><span class="cx">     if option == &quot;-h&quot;:
</span><span class="cx">         usage()
</span><span class="cx">         sys.exit(0)
</span><span class="cx">     elif option == &quot;--debug&quot;:
</span><span class="cx">         debug = True
</span><del>-    elif option == &quot;--router&quot;:
-        router = True
-    elif option == &quot;--worker&quot;:
-        worker = True
</del><span class="cx">     elif option == &quot;--lines&quot;:
</span><span class="cx">         numLines = int(value)
</span><span class="cx">     elif option == &quot;--range&quot;:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonsetuppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/setup.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/setup.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/setup.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -145,7 +145,6 @@
</span><span class="cx">                              &quot;bin/calendarserver_export&quot;,
</span><span class="cx">                             #&quot;bin/calendarserver_icalendar_validate&quot;,
</span><span class="cx">                             #&quot;bin/calendarserver_load_augmentdb&quot;,
</span><del>-                            #&quot;bin/calendarserver_make_partition&quot;,
</del><span class="cx">                             #&quot;bin/calendarserver_manage_postgres&quot;,
</span><span class="cx">                              &quot;bin/calendarserver_manage_principals&quot;,
</span><span class="cx">                              &quot;bin/calendarserver_manage_push&quot;,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsonsupportversionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/support/version.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/support/version.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/support/version.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx">     # Compute the version number.
</span><span class="cx">     #
</span><span class="cx"> 
</span><del>-    base_version = &quot;5.1&quot;
</del><ins>+    base_version = &quot;5.2&quot;
</ins><span class="cx"> 
</span><span class="cx">     branches = tuple(
</span><span class="cx">         branch.format(version=base_version)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextenterprisedalsyntaxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/syntax.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/syntax.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/syntax.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1687,6 +1687,48 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class DatabaseLock(_LockingStatement):
+    &quot;&quot;&quot;
+    An SQL exclusive session level advisory lock
+    &quot;&quot;&quot;
+
+    def _toSQL(self, queryGenerator):
+        assert(queryGenerator.dialect == POSTGRES_DIALECT)
+        return SQLFragment('select pg_advisory_lock(1)')
+
+
+    def on(self, txn, *a, **kw):
+        &quot;&quot;&quot;
+        Override on() to only execute on Postgres
+        &quot;&quot;&quot;
+        if txn.dialect == POSTGRES_DIALECT:
+            return super(DatabaseLock, self).on(txn, *a, **kw)
+
+        return succeed(None)
+
+
+
+class DatabaseUnlock(_LockingStatement):
+    &quot;&quot;&quot;
+    An SQL exclusive session level advisory lock
+    &quot;&quot;&quot;
+
+    def _toSQL(self, queryGenerator):
+        assert(queryGenerator.dialect == POSTGRES_DIALECT)
+        return SQLFragment('select pg_advisory_unlock(1)')
+
+
+    def on(self, txn, *a, **kw):
+        &quot;&quot;&quot;
+        Override on() to only execute on Postgres
+        &quot;&quot;&quot;
+        if txn.dialect == POSTGRES_DIALECT:
+            return super(DatabaseUnlock, self).on(txn, *a, **kw)
+
+        return succeed(None)
+
+
+
</ins><span class="cx"> class Savepoint(_LockingStatement):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     An SQL 'savepoint' statement.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextenterprisedaltesttest_sqlsyntaxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/test/test_sqlsyntax.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/test/test_sqlsyntax.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/enterprise/dal/test/test_sqlsyntax.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -25,7 +25,8 @@
</span><span class="cx">     TableMismatch, Parameter, Max, Len, NotEnoughValues,
</span><span class="cx">     Savepoint, RollbackToSavepoint, ReleaseSavepoint, SavepointAction,
</span><span class="cx">     Union, Intersect, Except, SetExpression, DALError,
</span><del>-    ResultAliasSyntax, Count, QueryGenerator, ALL_COLUMNS)
</del><ins>+    ResultAliasSyntax, Count, QueryGenerator, ALL_COLUMNS,
+    DatabaseLock, DatabaseUnlock)
</ins><span class="cx"> from twext.enterprise.dal.syntax import FixedPlaceholder, NumericPlaceholder
</span><span class="cx"> from twext.enterprise.dal.syntax import Function
</span><span class="cx"> from twext.enterprise.dal.syntax import SchemaSyntax
</span><span class="lines">@@ -1314,6 +1315,22 @@
</span><span class="cx">                           SQLFragment(&quot;lock table FOO in exclusive mode&quot;))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def test_databaseLock(self):
+        &quot;&quot;&quot;
+        L{DatabaseLock} generates a ('pg_advisory_lock') statement
+        &quot;&quot;&quot;
+        self.assertEquals(DatabaseLock().toSQL(),
+                          SQLFragment(&quot;select pg_advisory_lock(1)&quot;))
+
+
+    def test_databaseUnlock(self):
+        &quot;&quot;&quot;
+        L{DatabaseUnlock} generates a ('pg_advisory_unlock') statement
+        &quot;&quot;&quot;
+        self.assertEquals(DatabaseUnlock().toSQL(),
+                          SQLFragment(&quot;select pg_advisory_unlock(1)&quot;))
+
+
</ins><span class="cx">     def test_savepoint(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         L{Savepoint} generates a ('savepoint') statement.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextenterprisefixturespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/enterprise/fixtures.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/enterprise/fixtures.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/enterprise/fixtures.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -76,8 +76,8 @@
</span><span class="cx"> 
</span><span class="cx"> def resultOf(deferred, propagate=False):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Add a callback and errback which will capture the result of a L{Deferred} in
-    a list, and return that list.  If 'propagate' is True, pass through the
</del><ins>+    Add a callback and errback which will capture the result of a L{Deferred}
+    in a list, and return that list.  If 'propagate' is True, pass through the
</ins><span class="cx">     results.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     results = []
</span><span class="lines">@@ -194,7 +194,6 @@
</span><span class="cx">         No implementation.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def callFromThread(self, thunk, *a, **kw):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         No implementation.
</span><span class="lines">@@ -223,14 +222,16 @@
</span><span class="cx">             self.factory = ConnectionFactory()
</span><span class="cx">             connect = self.factory.connect
</span><span class="cx">         self.connect = connect
</span><del>-        self.paused             = False
-        self.holders            = []
-        self.pool               = ConnectionPool(connect,
-                                                 maxConnections=2,
-                                                 dialect=self.dialect,
-                                                 paramstyle=self.paramstyle)
</del><ins>+        self.paused = False
+        self.holders = []
+        self.pool = ConnectionPool(
+            connect,
+            maxConnections=2,
+            dialect=self.dialect,
+            paramstyle=self.paramstyle
+        )
</ins><span class="cx">         self.pool._createHolder = self.makeAHolder
</span><del>-        self.clock              = self.pool.reactor = ClockWithThreads()
</del><ins>+        self.clock = self.pool.reactor = ClockWithThreads()
</ins><span class="cx">         self.pool.startService()
</span><span class="cx">         test.addCleanup(self.flushHolders)
</span><span class="cx"> 
</span><span class="lines">@@ -239,7 +240,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Flush all pending C{submit}s since C{pauseHolders} was called.  This
</span><span class="cx">         makes sure the service is stopped and the fake ThreadHolders are all
</span><del>-        executing their queues so failed tsets can exit cleanly.
</del><ins>+        executing their queues so failed tests can exit cleanly.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         self.paused = False
</span><span class="cx">         for holder in self.holders:
</span><span class="lines">@@ -551,6 +552,21 @@
</span><span class="cx">         self._connectResultQueue.append(thunk)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def willConnectTo(self):
+        &quot;&quot;&quot;
+        Queue a successful result for connect() and immediately add it as a
+        child to this L{ConnectionFactory}.
+
+        @return: a connection object
+        @rtype: L{FakeConnection}
+        &quot;&quot;&quot;
+        aConnection = FakeConnection(self)
+        def thunk():
+            return aConnection
+        self._connectResultQueue.append(thunk)
+        return aConnection
+
+
</ins><span class="cx">     def willFail(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Used by tests to queue a successful result for connect().
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextenterprisequeuepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/enterprise/queue.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/enterprise/queue.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/enterprise/queue.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -115,7 +115,7 @@
</span><span class="cx">     (in the worst case) pass from worker-&gt;controller-&gt;controller-&gt;worker.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def performWork(table, workID):
</del><ins>+    def performWork(table, workID): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param table: The table where work is waiting.
</span><span class="cx">         @type table: L{TableSyntax}
</span><span class="lines">@@ -149,7 +149,8 @@
</span><span class="cx">     NodeTable.addColumn(&quot;PORT&quot;, SQLType(&quot;integer&quot;, None))
</span><span class="cx">     NodeTable.addColumn(&quot;TIME&quot;, SQLType(&quot;timestamp&quot;, None)).setDefaultValue(
</span><span class="cx">         # Note: in the real data structure, this is actually a not-cleaned-up
</span><del>-        # sqlparse internal data structure, but it *should* look closer to this.
</del><ins>+        # sqlparse internal data structure, but it *should* look closer to
+        # this.
</ins><span class="cx">         ProcedureCall(&quot;timezone&quot;, [&quot;UTC&quot;, NamedValue('CURRENT_TIMESTAMP')])
</span><span class="cx">     )
</span><span class="cx">     for column in NodeTable.columns:
</span><span class="lines">@@ -370,7 +371,6 @@
</span><span class="cx">         will be taken care of by the job queueing machinery.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     @classmethod
</span><span class="cx">     def forTable(cls, table):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -677,8 +677,8 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, peerPool, boxReceiver=None, locator=None):
</span><del>-        super(ConnectionFromWorker, self).__init__(peerPool.schema, boxReceiver,
-                                                   locator)
</del><ins>+        super(ConnectionFromWorker, self).__init__(peerPool.schema,
+                                                   boxReceiver, locator)
</ins><span class="cx">         self.peerPool = peerPool
</span><span class="cx">         self._load = 0
</span><span class="cx"> 
</span><span class="lines">@@ -830,9 +830,9 @@
</span><span class="cx">             workItem = yield workItemClass.load(txn, workID)
</span><span class="cx">             if workItem.group is not None:
</span><span class="cx">                 yield NamedLock.acquire(txn, workItem.group)
</span><del>-            # TODO: what if we fail?  error-handling should be recorded someplace,
-            # the row should probably be marked, re-tries should be triggerable
-            # administratively.
</del><ins>+            # TODO: what if we fail?  error-handling should be recorded
+            # someplace, the row should probably be marked, re-tries should be
+            # triggerable administratively.
</ins><span class="cx">             yield workItem.delete()
</span><span class="cx">             # TODO: verify that workID is the primary key someplace.
</span><span class="cx">             yield workItem.doWork()
</span><span class="lines">@@ -865,9 +865,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
-
-
</del><span class="cx"> class WorkerFactory(Factory, object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Factory, to be used as the client to connect from the worker to the
</span><span class="lines">@@ -950,7 +947,7 @@
</span><span class="cx">         waiting for the transaction where that addition was completed to
</span><span class="cx">         commit, and asking the local node controller process to do the work.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        @passthru(self.workItemType.create(self.txn, **self.kw).addCallback)
</del><ins>+        created = self.workItemType.create(self.txn, **self.kw)
</ins><span class="cx">         def whenCreated(item):
</span><span class="cx">             self._whenProposed.callback(self)
</span><span class="cx">             @self.txn.postCommit
</span><span class="lines">@@ -967,12 +964,15 @@
</span><span class="cx">                         self._whenExecuted.errback(why)
</span><span class="cx">                 reactor = self._chooser.reactor
</span><span class="cx">                 when = max(0, astimestamp(item.notBefore) - reactor.seconds())
</span><del>-                # TODO: Track the returned DelayedCall so it can be stopped when
-                # the service stops.
</del><ins>+                # TODO: Track the returned DelayedCall so it can be stopped
+                # when the service stops.
</ins><span class="cx">                 self._chooser.reactor.callLater(when, maybeLater)
</span><span class="cx">             @self.txn.postAbort
</span><span class="cx">             def whenFailed():
</span><span class="cx">                 self._whenCommitted.errback(TransactionFailed)
</span><ins>+        def whenNotCreated(failure):
+            self._whenProposed.errback(failure)
+        created.addCallbacks(whenCreated, whenNotCreated)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def whenExecuted(self):
</span><span class="lines">@@ -1023,6 +1023,8 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return _cloneDeferred(self._whenCommitted)
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class _BaseQueuer(object):
</span><span class="cx">     implements(IQueuer)
</span><span class="cx"> 
</span><span class="lines">@@ -1030,13 +1032,16 @@
</span><span class="cx">         super(_BaseQueuer, self).__init__()
</span><span class="cx">         self.proposalCallbacks = set()
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def callWithNewProposals(self, callback):
</span><del>-        self.proposalCallbacks.add(callback);
</del><ins>+        self.proposalCallbacks.add(callback)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def transferProposalCallbacks(self, newQueuer):
</span><span class="cx">         newQueuer.proposalCallbacks = self.proposalCallbacks
</span><span class="cx">         return newQueuer
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def enqueueWork(self, txn, workItemType, **kw):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         There is some work to do.  Do it, someplace else, ideally in parallel.
</span><span class="lines">@@ -1061,6 +1066,7 @@
</span><span class="cx">         return wp
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class PeerConnectionPool(_BaseQueuer, MultiService, object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Each node has a L{PeerConnectionPool} connecting it to all the other nodes
</span><span class="lines">@@ -1140,7 +1146,7 @@
</span><span class="cx">         self.mappedPeers = {}
</span><span class="cx">         self.schema = schema
</span><span class="cx">         self._startingUp = None
</span><del>-        self._listeningPortObject = None
</del><ins>+        self._listeningPort = None
</ins><span class="cx">         self._lastSeenTotalNodes = 1
</span><span class="cx">         self._lastSeenNodeIndex = 1
</span><span class="cx"> 
</span><span class="lines">@@ -1197,7 +1203,8 @@
</span><span class="cx">         A peer has requested us to perform some work; choose a work performer
</span><span class="cx">         local to this node, and then execute it.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        return self.choosePerformer(onlyLocally=True).performWork(table, workID)
</del><ins>+        performer = self.choosePerformer(onlyLocally=True)
+        return performer.performWork(table, workID)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def allWorkItemTypes(self):
</span><span class="lines">@@ -1225,8 +1232,8 @@
</span><span class="cx"> 
</span><span class="cx">         @return: the maximum number of other L{PeerConnectionPool} instances
</span><span class="cx">             that may be connected to the database described by
</span><del>-            C{self.transactionFactory}.  Note that this is not the current count
-            by connectivity, but the count according to the database.
</del><ins>+            C{self.transactionFactory}.  Note that this is not the current
+            count by connectivity, but the count according to the database.
</ins><span class="cx">         @rtype: L{int}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         # TODO
</span><span class="lines">@@ -1277,7 +1284,6 @@
</span><span class="cx">                                            overdueItem.workID)
</span><span class="cx">         return inTransaction(self.transactionFactory, workCheck)
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     _currentWorkDeferred = None
</span><span class="cx">     _lostWorkCheckCall = None
</span><span class="cx"> 
</span><span class="lines">@@ -1315,10 +1321,10 @@
</span><span class="cx">         @inlineCallbacks
</span><span class="cx">         def startup(txn):
</span><span class="cx">             endpoint = TCP4ServerEndpoint(self.reactor, self.ampPort)
</span><del>-            # If this fails, the failure mode is going to be ugly, just like all
-            # conflicted-port failures.  But, at least it won't proceed.
-            self._listeningPortObject = yield endpoint.listen(self.peerFactory())
-            self.ampPort = self._listeningPortObject.getHost().port
</del><ins>+            # If this fails, the failure mode is going to be ugly, just like
+            # all conflicted-port failures.  But, at least it won't proceed.
+            self._listeningPort = yield endpoint.listen(self.peerFactory())
+            self.ampPort = self._listeningPort.getHost().port
</ins><span class="cx">             yield Lock.exclusive(NodeInfo.table).on(txn)
</span><span class="cx">             nodes = yield self.activeNodes(txn)
</span><span class="cx">             selves = [node for node in nodes
</span><span class="lines">@@ -1354,8 +1360,8 @@
</span><span class="cx">         yield super(PeerConnectionPool, self).stopService()
</span><span class="cx">         if self._startingUp is not None:
</span><span class="cx">             yield self._startingUp
</span><del>-        if self._listeningPortObject is not None:
-            yield self._listeningPortObject.stopListening()
</del><ins>+        if self._listeningPort is not None:
+            yield self._listeningPort.stopListening()
</ins><span class="cx">         if self._lostWorkCheckCall is not None:
</span><span class="cx">             self._lostWorkCheckCall.cancel()
</span><span class="cx">         if self._currentWorkDeferred is not None:
</span><span class="lines">@@ -1430,8 +1436,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
-
</del><span class="cx"> class LocalQueuer(_BaseQueuer):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     When work is enqueued with this queuer, it is just executed locally.
</span><span class="lines">@@ -1458,7 +1462,8 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Implementor of C{performWork} that doesn't actual perform any work.  This
</span><span class="cx">     is used in the case where you want to be able to enqueue work for someone
</span><del>-    else to do, but not take on any work yourself (such as a command line tool).
</del><ins>+    else to do, but not take on any work yourself (such as a command line
+    tool).
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     implements(_IWorkPerformer)
</span><span class="cx"> 
</span><span class="lines">@@ -1469,6 +1474,7 @@
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class NonPerformingQueuer(_BaseQueuer):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     When work is enqueued with this queuer, it is never executed locally.
</span><span class="lines">@@ -1487,4 +1493,4 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Choose to perform the work locally.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        return NonPerformer()
</del><span class="cx">\ No newline at end of file
</span><ins>+        return NonPerformer()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextenterprisetesttest_queuepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/enterprise/test/test_queue.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/enterprise/test/test_queue.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/enterprise/test/test_queue.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><ins>+from twisted.python.failure import Failure
</ins><span class="cx"> from twisted.internet.defer import (
</span><span class="cx">     Deferred, inlineCallbacks, gatherResults, passthru#, returnValue
</span><span class="cx"> )
</span><span class="lines">@@ -55,6 +56,8 @@
</span><span class="cx"> from twisted.test.proto_helpers import StringTransport, MemoryReactor
</span><span class="cx"> from twext.enterprise.fixtures import SteppablePoolHelper
</span><span class="cx"> from twisted.internet.defer import returnValue
</span><ins>+from twext.enterprise.queue import LocalQueuer
+from twext.enterprise.fixtures import ConnectionPoolHelper
</ins><span class="cx"> 
</span><span class="cx"> from twext.enterprise.queue import _BaseQueuer, NonPerformingQueuer
</span><span class="cx"> import twext.enterprise.queue
</span><span class="lines">@@ -67,7 +70,7 @@
</span><span class="cx"> 
</span><span class="cx">     def callLater(self, _seconds, _f, *args, **kw):
</span><span class="cx">         if _seconds &lt; 0:
</span><del>-            raise ValueError(&quot;%s&lt;0: &quot;%(_seconds,))
</del><ins>+            raise ValueError(&quot;%s&lt;0: &quot; % (_seconds,))
</ins><span class="cx">         return super(Clock, self).callLater(_seconds, _f, *args, **kw)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -267,6 +270,56 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class WorkProposalTests(TestCase):
+    &quot;&quot;&quot;
+    Tests for L{WorkProposal}.
+    &quot;&quot;&quot;
+
+    def test_whenProposedSuccess(self):
+        &quot;&quot;&quot;
+        The L{Deferred} returned by L{WorkProposal.whenProposed} fires when the
+        SQL sent to the database has completed.
+        &quot;&quot;&quot;
+        cph = ConnectionPoolHelper()
+        cph.setUp(test=self)
+        cph.pauseHolders()
+        lq = LocalQueuer(cph.createTransaction)
+        enqTxn = cph.createTransaction()
+        wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+        d = wp.whenProposed()
+        r = cph.resultOf(d)
+        self.assertEquals(r, [])
+        cph.flushHolders()
+        self.assertEquals(len(r), 1)
+
+
+    def test_whenProposedFailure(self):
+        &quot;&quot;&quot;
+        The L{Deferred} returned by L{WorkProposal.whenProposed} fails with an
+        errback when the SQL executed to create the WorkItem row fails.
+        &quot;&quot;&quot;
+        cph = ConnectionPoolHelper()
+        cph.setUp(self)
+        cph.pauseHolders()
+        firstConnection = cph.factory.willConnectTo()
+        enqTxn = cph.createTransaction()
+        # Execute some SQL on the connection before enqueueing the work-item so
+        # that we don't get the initial-statement.
+        enqTxn.execSQL(&quot;some sql&quot;)
+        lq = LocalQueuer(cph.createTransaction)
+        cph.flushHolders()
+        cph.pauseHolders()
+        wp = lq.enqueueWork(enqTxn, DummyWorkItem, a=3, b=4)
+        firstConnection.executeWillFail(lambda: RuntimeError(&quot;foo&quot;))
+        d = wp.whenProposed()
+        r = cph.resultOf(d)
+        self.assertEquals(r, [])
+        cph.flushHolders()
+        self.assertEquals(len(r), 1)
+        self.assertIsInstance(r[0], Failure)
+
+
+
</ins><span class="cx"> class PeerConnectionPoolUnitTests(TestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     L{PeerConnectionPool} has many internal components.
</span><span class="lines">@@ -393,7 +446,8 @@
</span><span class="cx">             # Next, create one that's actually far enough into the past to run.
</span><span class="cx">             yield DummyWorkItem.create(
</span><span class="cx">                 txn, a=3, b=4, notBefore=(
</span><del>-                    # Schedule it in the past so that it should have already run.
</del><ins>+                    # Schedule it in the past so that it should have already
+                    # run.
</ins><span class="cx">                     fakeNow - datetime.timedelta(
</span><span class="cx">                         seconds=qpool.queueProcessTimeout + 20
</span><span class="cx">                     )
</span><span class="lines">@@ -509,8 +563,8 @@
</span><span class="cx">             t = StringTransport()
</span><span class="cx">             p.makeConnection(t)
</span><span class="cx">             return p, t
</span><del>-        worker1, trans1 = peer()
-        worker2, trans2 = peer()
</del><ins>+        worker1, _ignore_trans1 = peer()
+        worker2, _ignore_trans2 = peer()
</ins><span class="cx">         # Ask the worker to do something.
</span><span class="cx">         worker1.performWork(schema.DUMMY_WORK_ITEM, 1)
</span><span class="cx">         self.assertEquals(worker1.currentLoad, 1)
</span><span class="lines">@@ -619,11 +673,12 @@
</span><span class="cx">         self.receiver, self.sender = self.sender, self.receiver
</span><span class="cx">         return result
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def flush(self, turns=10):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Keep relaying data until there's no more.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        for x in range(turns):
</del><ins>+        for _ignore_x in range(turns):
</ins><span class="cx">             if not (self.pump() or self.pump()):
</span><span class="cx">                 return
</span><span class="cx"> 
</span><span class="lines">@@ -718,7 +773,7 @@
</span><span class="cx">         def op2(txn):
</span><span class="cx">             return Select([schema.DUMMY_WORK_DONE.WORK_ID,
</span><span class="cx">                            schema.DUMMY_WORK_DONE.A_PLUS_B],
</span><del>-                           From=schema.DUMMY_WORK_DONE).on(txn)
</del><ins>+                          From=schema.DUMMY_WORK_DONE).on(txn)
</ins><span class="cx">         rows = yield inTransaction(self.store.newTransaction, op2)
</span><span class="cx">         self.assertEquals(rows, [[4321, 7]])
</span><span class="cx"> 
</span><span class="lines">@@ -729,7 +784,7 @@
</span><span class="cx">         When a L{WorkItem} is concurrently deleted by another transaction, it
</span><span class="cx">         should I{not} perform its work.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        # Provide access to a method called 'concurrently' everything using 
</del><ins>+        # Provide access to a method called 'concurrently' everything using
</ins><span class="cx">         original = self.store.newTransaction
</span><span class="cx">         def decorate(*a, **k):
</span><span class="cx">             result = original(*a, **k)
</span><span class="lines">@@ -746,13 +801,13 @@
</span><span class="cx">         # Sanity check on the concurrent deletion.
</span><span class="cx">         def op2(txn):
</span><span class="cx">             return Select([schema.DUMMY_WORK_ITEM.WORK_ID],
</span><del>-                           From=schema.DUMMY_WORK_ITEM).on(txn)
</del><ins>+                          From=schema.DUMMY_WORK_ITEM).on(txn)
</ins><span class="cx">         rows = yield inTransaction(self.store.newTransaction, op2)
</span><span class="cx">         self.assertEquals(rows, [])
</span><span class="cx">         def op3(txn):
</span><span class="cx">             return Select([schema.DUMMY_WORK_DONE.WORK_ID,
</span><span class="cx">                            schema.DUMMY_WORK_DONE.A_PLUS_B],
</span><del>-                           From=schema.DUMMY_WORK_DONE).on(txn)
</del><ins>+                          From=schema.DUMMY_WORK_DONE).on(txn)
</ins><span class="cx">         rows = yield inTransaction(self.store.newTransaction, op3)
</span><span class="cx">         self.assertEquals(rows, [])
</span><span class="cx"> 
</span><span class="lines">@@ -763,18 +818,23 @@
</span><span class="cx">     def __init__(self, *ignored):
</span><span class="cx">         pass
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _start(self):
</span><span class="cx">         pass
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class BaseQueuerTests(TestCase):
</span><span class="cx"> 
</span><span class="cx">     def setUp(self):
</span><span class="cx">         self.proposal = None
</span><span class="cx">         self.patch(twext.enterprise.queue, &quot;WorkProposal&quot;, DummyProposal)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _proposalCallback(self, proposal):
</span><span class="cx">         self.proposal = proposal
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def test_proposalCallbacks(self):
</span><span class="cx">         queuer = _BaseQueuer()
</span><span class="cx">         queuer.callWithNewProposals(self._proposalCallback)
</span><span class="lines">@@ -783,6 +843,7 @@
</span><span class="cx">         self.assertNotEqual(self.proposal, None)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class NonPerformingQueuerTests(TestCase):
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -791,5 +852,3 @@
</span><span class="cx">         performer = queuer.choosePerformer()
</span><span class="cx">         result = (yield performer.performWork(None, None))
</span><span class="cx">         self.assertEquals(result, None)
</span><del>-
-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextpatchespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/patches.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/patches.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/patches.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -26,6 +26,8 @@
</span><span class="cx"> from twisted.python.versions import Version
</span><span class="cx"> from twisted.python.modules import getModule
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> def _hasIPv6ClientSupport():
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Does the loaded version of Twisted have IPv6 client support?
</span><span class="lines">@@ -34,8 +36,9 @@
</span><span class="cx">     if version &gt; lastVersionWithoutIPv6Clients:
</span><span class="cx">         return True
</span><span class="cx">     elif version == lastVersionWithoutIPv6Clients:
</span><del>-        # It could be a snapshot of trunk or a branch with this bug fixed. Don't
-        # load the module, though, as that would be a bunch of unnecessary work.
</del><ins>+        # It could be a snapshot of trunk or a branch with this bug fixed.
+        # Don't load the module, though, as that would be a bunch of
+        # unnecessary work.
</ins><span class="cx">         return &quot;_resolveIPv6&quot; in (getModule(&quot;twisted.internet.tcp&quot;)
</span><span class="cx">                                   .filePath.getContent())
</span><span class="cx">     else:
</span><span class="lines">@@ -45,8 +48,8 @@
</span><span class="cx"> 
</span><span class="cx"> def _addBackports():
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    We currently require 2 backported bugfixes from a future release of Twisted,
-    for IPv6 support:
</del><ins>+    We currently require 2 backported bugfixes from a future release of
+    Twisted, for IPv6 support:
</ins><span class="cx"> 
</span><span class="cx">         - U{IPv6 client support &lt;http://tm.tl/5085&gt;}
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextprotocolstesttest_memcachepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/protocols/test/test_memcache.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/protocols/test/test_memcache.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/protocols/test/test_memcache.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -14,7 +14,17 @@
</span><span class="cx"> from twisted.internet.defer import Deferred, gatherResults, TimeoutError
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def onConnectionLossFire(protocol, deferred):
+    &quot;&quot;&quot;
+    When the given L{MemCacheProtocol} is disconnected, fire the given
+    L{Deferred} with L{None}.
+    &quot;&quot;&quot;
+    def cl(reason):
+        deferred.callback(None)
+    protocol.connectionLost = cl
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class MemCacheTestCase(TestCase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Test client protocol class L{MemCacheProtocol}.
</span><span class="lines">@@ -244,7 +254,7 @@
</span><span class="cx">         d1 = self.proto.get(&quot;foo&quot;)
</span><span class="cx">         d2 = self.proto.get(&quot;bar&quot;)
</span><span class="cx">         d3 = Deferred()
</span><del>-        self.proto.connectionLost = d3.callback
</del><ins>+        onConnectionLossFire(self.proto, d3)
</ins><span class="cx"> 
</span><span class="cx">         self.clock.advance(self.proto.persistentTimeOut)
</span><span class="cx">         self.assertFailure(d1, TimeoutError)
</span><span class="lines">@@ -280,7 +290,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         d1 = self.proto.get(&quot;foo&quot;)
</span><span class="cx">         d2 = Deferred()
</span><del>-        self.proto.connectionLost = d2.callback
</del><ins>+        onConnectionLossFire(self.proto, d2)
</ins><span class="cx"> 
</span><span class="cx">         self.proto.dataReceived(&quot;VALUE foo 0 10\r\n12345&quot;)
</span><span class="cx">         self.clock.advance(self.proto.persistentTimeOut)
</span><span class="lines">@@ -295,7 +305,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         d1 = self.proto.stats()
</span><span class="cx">         d2 = Deferred()
</span><del>-        self.proto.connectionLost = d2.callback
</del><ins>+        onConnectionLossFire(self.proto, d2)
</ins><span class="cx"> 
</span><span class="cx">         self.proto.dataReceived(&quot;STAT foo bar\r\n&quot;)
</span><span class="cx">         self.clock.advance(self.proto.persistentTimeOut)
</span><span class="lines">@@ -311,7 +321,7 @@
</span><span class="cx">         d1 = self.proto.get(&quot;foo&quot;)
</span><span class="cx">         d2 = self.proto.get(&quot;bar&quot;)
</span><span class="cx">         d3 = Deferred()
</span><del>-        self.proto.connectionLost = d3.callback
</del><ins>+        onConnectionLossFire(self.proto, d3)
</ins><span class="cx"> 
</span><span class="cx">         self.clock.advance(self.proto.persistentTimeOut - 1)
</span><span class="cx">         self.proto.dataReceived(&quot;VALUE foo 0 3\r\nbar\r\nEND\r\n&quot;)
</span><span class="lines">@@ -319,7 +329,7 @@
</span><span class="cx">         def check(result):
</span><span class="cx">             self.assertEquals(result, (0, &quot;bar&quot;))
</span><span class="cx">             self.assertEquals(len(self.clock.calls), 1)
</span><del>-            for i in range(self.proto.persistentTimeOut):
</del><ins>+            for _ignore_i in range(self.proto.persistentTimeOut):
</ins><span class="cx">                 self.clock.advance(1)
</span><span class="cx">             return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
</span><span class="cx">         def checkTime(ignored):
</span><span class="lines">@@ -338,7 +348,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         d1 = self.proto.get(&quot;foo&quot;)
</span><span class="cx">         d3 = Deferred()
</span><del>-        self.proto.connectionLost = d3.callback
</del><ins>+        onConnectionLossFire(self.proto, d3)
</ins><span class="cx"> 
</span><span class="cx">         self.clock.advance(self.proto.persistentTimeOut - 1)
</span><span class="cx">         d2 = self.proto.get(&quot;bar&quot;)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoaggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/aggregate.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/aggregate.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/aggregate.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -45,13 +45,16 @@
</span><span class="cx"> 
</span><span class="cx">         for service in services:
</span><span class="cx">             if not IDirectoryService.implementedBy(service.__class__):
</span><del>-                raise ValueError(&quot;Not a directory service: %s&quot; % (service,))
</del><ins>+                raise ValueError(
+                    &quot;Not a directory service: {0}&quot;.format(service)
+                )
</ins><span class="cx"> 
</span><span class="cx">             for recordType in service.recordTypes():
</span><span class="cx">                 if recordType in recordTypes:
</span><span class="cx">                     raise DirectoryConfigurationError(
</span><del>-                        &quot;Aggregated services may not vend the same record type: %s&quot;
-                        % (recordType,)
</del><ins>+                        &quot;Aggregated services may not vend &quot;
+                        &quot;the same record type: {0}&quot;
+                        .format(recordType)
</ins><span class="cx">                     )
</span><span class="cx">                 recordTypes.add(recordType)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/directory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/directory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/directory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -24,10 +24,8 @@
</span><span class="cx">     &quot;DirectoryRecord&quot;,
</span><span class="cx"> ]
</span><span class="cx"> 
</span><del>-from uuid import UUID
</del><ins>+from zope.interface import implementer
</ins><span class="cx"> 
</span><del>-from zope.interface import implements
-
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from twisted.internet.defer import succeed, fail
</span><span class="cx"> 
</span><span class="lines">@@ -40,26 +38,69 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@implementer(IDirectoryService)
</ins><span class="cx"> class DirectoryService(object):
</span><del>-    implements(IDirectoryService)
</del><ins>+    &quot;&quot;&quot;
+    Generic implementation of L{IDirectoryService}.
</ins><span class="cx"> 
</span><ins>+    This is a complete implementation of L{IDirectoryService}, with support for
+    the query operands in L{Operand}.
+
+    The C{recordsWith*} methods are all implemented in terms of
+    L{recordsWithFieldValue}, which is in turn implemented in terms of
+    L{recordsFromExpression}.
+    L{recordsFromQuery} is also implemented in terms of
+    {recordsFromExpression}.
+
+    L{recordsFromExpression} (and therefore most uses of the other methods)
+    will always fail with a L{QueryNotSupportedError}.
+
+    A subclass should therefore override L{recordsFromExpression} with an
+    implementation that handles any queries that it can support and its
+    superclass' implementation with any query it cannot support.
+
+    A subclass may override L{recordsFromQuery} if it is to support additional
+    operands.
+
+    L{updateRecords} and L{removeRecords} will fail with L{NotAllowedError}
+    when asked to modify data.
+    A subclass should override these methods if is to allow editing of
+    directory information.
+
+    @cvar recordType: a L{Names} class or compatible object (eg.
+        L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+        the record types that are supported by this directory service.
+
+    @cvar fieldName: a L{Names} class or compatible object (eg.
+        L{ConstantsContainer}) which contains the L{NamedConstant}s denoting
+        the record field names that are supported by this directory service.
+
+    @cvar normalizedFields: a L{dict} mapping of (ie. L{NamedConstant}s
+        contained in the C{fieldName} class variable) to callables that take
+        a field value (a L{unicode}) and return a normalized field value (also
+        a L{unicode}).
+    &quot;&quot;&quot;
+
</ins><span class="cx">     recordType = RecordType
</span><span class="cx">     fieldName  = FieldName
</span><span class="cx"> 
</span><span class="cx">     normalizedFields = {
</span><del>-        FieldName.guid:           lambda g: UUID(g).hex,
-        FieldName.emailAddresses: lambda e: e.lower(),
</del><ins>+        FieldName.emailAddresses: lambda e: bytes(e).lower(),
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, realmName):
</span><ins>+        &quot;&quot;&quot;
+        @param realmName: a realm name
+        @type realmName: unicode
+        &quot;&quot;&quot;
</ins><span class="cx">         self.realmName = realmName
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def __repr__(self):
</span><del>-        return &quot;&lt;%s %r&gt;&quot; % (
-            self.__class__.__name__,
-            self.realmName,
</del><ins>+        return (
+            &quot;&lt;{self.__class__.__name__} {self.realmName!r}&gt;&quot;
+            .format(self=self)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -70,13 +111,34 @@
</span><span class="cx">     def recordsFromExpression(self, expression, records=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Finds records matching a single expression.
</span><del>-        @param expression: an expression
</del><ins>+
+        @note: The implementation in L{DirectoryService} always raises
+            L{QueryNotSupportedError}.
+
+        @note: This L{DirectoryService} adds a C{records} keyword argument to
+            the interface defined by L{IDirectoryService}.
+            This allows the implementation of
+            L{DirectoryService.recordsFromQuery} to narrow the scope of records
+            being searched as it applies expressions.
+            This is therefore relevant to subclasses, which need to support the
+            added parameter, but not to users of L{IDirectoryService}.
+
+        @param expression: an expression to apply
</ins><span class="cx">         @type expression: L{object}
</span><del>-        @param records: a set of records to search within. C{None} if
</del><ins>+
+        @param records: a set of records to limit the search to. C{None} if
</ins><span class="cx">             the whole directory should be searched.
</span><span class="cx">         @type records: L{set} or L{frozenset}
</span><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
+
+        @raises: L{QueryNotSupportedError} if the expression is not
+            supported by this directory service.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        return fail(QueryNotSupportedError(&quot;Unknown expression: %s&quot; % (expression,)))
</del><ins>+        return fail(QueryNotSupportedError(
+            &quot;Unknown expression: {0}&quot;.format(expression)
+        ))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -109,7 +171,9 @@
</span><span class="cx">             elif operand == Operand.OR:
</span><span class="cx">                 results |= recordsMatchingExpression
</span><span class="cx">             else:
</span><del>-                raise QueryNotSupportedError(&quot;Unknown operand: %s&quot; % (operand,))
</del><ins>+                raise QueryNotSupportedError(
+                    &quot;Unknown operand: {0}&quot;.format(operand)
+                )
</ins><span class="cx"> 
</span><span class="cx">         returnValue(results)
</span><span class="cx"> 
</span><span class="lines">@@ -120,12 +184,16 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def recordWithUID(self, uid):
</span><del>-        returnValue(uniqueResult((yield self.recordsWithFieldValue(FieldName.uid, uid))))
-               
</del><ins>+        returnValue(uniqueResult(
+            (yield self.recordsWithFieldValue(FieldName.uid, uid))
+        ))
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def recordWithGUID(self, guid):
</span><del>-        returnValue(uniqueResult((yield self.recordsWithFieldValue(FieldName.guid, guid))))
</del><ins>+        returnValue(uniqueResult(
+            (yield self.recordsWithFieldValue(FieldName.guid, guid))
+        ))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordsWithRecordType(self, recordType):
</span><span class="lines">@@ -136,28 +204,46 @@
</span><span class="cx">     def recordWithShortName(self, recordType, shortName):
</span><span class="cx">         returnValue(uniqueResult((yield self.recordsFromQuery((
</span><span class="cx">             MatchExpression(FieldName.recordType, recordType),
</span><del>-            MatchExpression(FieldName.shortNames, shortName ),
</del><ins>+            MatchExpression(FieldName.shortNames, shortName),
</ins><span class="cx">         )))))
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordsWithEmailAddress(self, emailAddress):
</span><del>-        return self.recordsWithFieldValue(FieldName.emailAddresses, emailAddress)
</del><ins>+        return self.recordsWithFieldValue(
+            FieldName.emailAddresses,
+            emailAddress,
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def updateRecords(self, records, create=False):
</span><span class="cx">         for record in records:
</span><span class="cx">             return fail(NotAllowedError(&quot;Record updates not allowed.&quot;))
</span><ins>+        return succeed(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def removeRecords(self, uids):
</span><span class="cx">         for uid in uids:
</span><span class="cx">             return fail(NotAllowedError(&quot;Record removal not allowed.&quot;))
</span><ins>+        return succeed(None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+@implementer(IDirectoryRecord)
</ins><span class="cx"> class DirectoryRecord(object):
</span><del>-    implements(IDirectoryRecord)
</del><ins>+    &quot;&quot;&quot;
+    Generic implementation of L{IDirectoryService}.
</ins><span class="cx"> 
</span><ins>+    This is an incomplete implementation of L{IDirectoryRecord}.
+
+    L{groups} will always fail with L{NotImplementedError} and L{members} will
+    do so if this is a group record.
+    A subclass should override these methods to support group membership and
+    complete this implementation.
+
+    @cvar requiredFields: an iterable of field names that must be present in
+        all directory records.
+    &quot;&quot;&quot;
+
</ins><span class="cx">     requiredFields = (
</span><span class="cx">         FieldName.uid,
</span><span class="cx">         FieldName.recordType,
</span><span class="lines">@@ -168,21 +254,31 @@
</span><span class="cx">     def __init__(self, service, fields):
</span><span class="cx">         for fieldName in self.requiredFields:
</span><span class="cx">             if fieldName not in fields or not fields[fieldName]:
</span><del>-                raise ValueError(&quot;%s field is required.&quot; % (fieldName,))
</del><ins>+                raise ValueError(&quot;{0} field is required.&quot;.format(fieldName))
</ins><span class="cx"> 
</span><span class="cx">             if FieldName.isMultiValue(fieldName):
</span><span class="cx">                 values = fields[fieldName]
</span><span class="cx">                 if len(values) == 0:
</span><del>-                    raise ValueError(&quot;%s field must have at least one value.&quot; % (fieldName,))
</del><ins>+                    raise ValueError(
+                        &quot;{0} field must have at least one value.&quot;
+                        .format(fieldName)
+                    )
</ins><span class="cx">                 for value in values:
</span><span class="cx">                     if not value:
</span><del>-                        raise ValueError(&quot;%s field must not be empty.&quot; % (fieldName,))
</del><ins>+                        raise ValueError(
+                            &quot;{0} field must not be empty.&quot;.format(fieldName)
+                        )
</ins><span class="cx"> 
</span><del>-        if fields[FieldName.recordType] not in service.recordType.iterconstants():
-            raise ValueError(&quot;Record type must be one of %r, not %r.&quot; % (
-                tuple(service.recordType.iterconstants()),
-                fields[FieldName.recordType]
-            ))
</del><ins>+        if (
+            fields[FieldName.recordType] not in
+            service.recordType.iterconstants()
+        ):
+            raise ValueError(
+                &quot;Record type must be one of {0!r}, not {1!r}.&quot;.format(
+                    tuple(service.recordType.iterconstants()),
+                    fields[FieldName.recordType],
+                )
+            )
</ins><span class="cx"> 
</span><span class="cx">         # Normalize fields
</span><span class="cx">         normalizedFields = {}
</span><span class="lines">@@ -197,16 +293,18 @@
</span><span class="cx">                 normalizedFields[name] = tuple((normalize(v) for v in value))
</span><span class="cx">             else:
</span><span class="cx">                 normalizedFields[name] = normalize(value)
</span><del>-        
</del><ins>+
</ins><span class="cx">         self.service = service
</span><span class="cx">         self.fields  = normalizedFields
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def __repr__(self):
</span><del>-        return &quot;&lt;%s (%s)%s&gt;&quot; % (
-            self.__class__.__name__,
-            describe(self.recordType),
-            self.shortNames[0],
</del><ins>+        return (
+            &quot;&lt;{self.__class__.__name__} ({recordType}){shortName}&gt;&quot;.format(
+                self=self,
+                recordType=describe(self.recordType),
+                shortName=self.shortNames[0],
+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -262,9 +360,11 @@
</span><span class="cx"> 
</span><span class="cx">     def members(self):
</span><span class="cx">         if self.recordType == RecordType.group:
</span><del>-            raise NotImplementedError()
</del><ins>+            return fail(
+                NotImplementedError(&quot;Subclasses must implement members()&quot;)
+            )
</ins><span class="cx">         return succeed(())
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groups(self):
</span><del>-        raise NotImplementedError()
</del><ins>+        return fail(NotImplementedError(&quot;Subclasses must implement groups()&quot;))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoexpressionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/expression.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/expression.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/expression.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -66,18 +66,26 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Query for a matching value in a given field.
</span><span class="cx"> 
</span><del>-    @ivar fieldName: a L{NamedConstant} specifying the field
-    @ivar fieldValue: a text value to match
-    @ivar matchType: a L{NamedConstant} specifying the match algorythm
-    @ivar flags: L{NamedConstant} specifying additional options
</del><ins>+    @ivar fieldName: A L{NamedConstant} specifying the field.
+
+    @ivar fieldValue: A value to match.
+
+    @ivar matchType: A L{NamedConstant} specifying the match algorithm.
+
+    @ivar flags: A L{NamedConstant} specifying additional options.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, fieldName, fieldValue, matchType=MatchType.equals, flags=None):
</del><ins>+    def __init__(
+        self,
+        fieldName, fieldValue,
+        matchType=MatchType.equals, flags=None
+    ):
</ins><span class="cx">         self.fieldName  = fieldName
</span><span class="cx">         self.fieldValue = fieldValue
</span><span class="cx">         self.matchType  = matchType
</span><span class="cx">         self.flags      = flags
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def __repr__(self):
</span><span class="cx">         def describe(constant):
</span><span class="cx">             return getattr(constant, &quot;description&quot;, str(constant))
</span><span class="lines">@@ -85,12 +93,16 @@
</span><span class="cx">         if self.flags is None:
</span><span class="cx">             flags = &quot;&quot;
</span><span class="cx">         else:
</span><del>-            flags = &quot; (%s)&quot; % (describe(self.flags),)
</del><ins>+            flags = &quot; ({0})&quot;.format(describe(self.flags))
</ins><span class="cx"> 
</span><del>-        return &quot;&lt;%s: %r %s %r%s&gt;&quot; % (
-            self.__class__.__name__,
-            describe(self.fieldName),
-            describe(self.matchType),
-            describe(self.fieldValue),
-            flags
</del><ins>+        return (
+            &quot;&lt;{self.__class__.__name__}: {fieldName!r} &quot;
+            &quot;{matchType} {fieldValue!r}{flags}&gt;&quot;
+            .format(
+                self=self,
+                fieldName=describe(self.fieldName),
+                matchType=describe(self.matchType),
+                fieldValue=describe(self.fieldValue),
+                flags=flags,
+            )
</ins><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoidirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/idirectory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/idirectory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/idirectory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -36,31 +36,39 @@
</span><span class="cx">     &quot;IDirectoryRecord&quot;,
</span><span class="cx"> ]
</span><span class="cx"> 
</span><ins>+from uuid import UUID
+
</ins><span class="cx"> from zope.interface import Attribute, Interface
</span><span class="cx"> 
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Exceptions
</span><del>-##
</del><ins>+#
</ins><span class="cx"> 
</span><span class="cx"> class DirectoryServiceError(Exception):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Directory service generic error.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class DirectoryConfigurationError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Directory configurtion error.
</del><ins>+    Directory configuration error.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class DirectoryAvailabilityError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Directory not available.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class UnknownRecordTypeError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Unknown record type.
</span><span class="lines">@@ -69,28 +77,37 @@
</span><span class="cx">         DirectoryServiceError.__init__(self, token)
</span><span class="cx">         self.token = token
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class QueryNotSupportedError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Query not supported.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class NoSuchRecordError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Record does not exist.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class NotAllowedError(DirectoryServiceError):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Apparently, you can't do that.
</del><ins>+    It seems you aren't permitted to do that.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Data Types
</span><del>-##
</del><ins>+#
</ins><span class="cx"> 
</span><span class="cx"> class RecordType(Names):
</span><ins>+    &quot;&quot;&quot;
+    Constants for common directory record types.
+    &quot;&quot;&quot;
</ins><span class="cx">     user  = NamedConstant()
</span><span class="cx">     group = NamedConstant()
</span><span class="cx"> 
</span><span class="lines">@@ -101,7 +118,31 @@
</span><span class="cx"> 
</span><span class="cx"> class FieldName(Names):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Constants for common field names.
</del><ins>+    Constants for common directory record field names.
+
+    Fields as assciated with either a single value or an iterable of values.
+
+    @cvar uid: The primary unique identifier for a directory record.
+        The associated value must be a L{unicode}.
+
+    @cvar guid: The globally unique identifier for a directory record.
+        The associated value must be a L{UUID} or C{None}.
+
+    @cvar recordType: The type of a directory record.
+        The associated value must be a L{NamedConstant}.
+
+    @cvar shortNames: The short names for a directory record.
+        The associated values must L{unicode}s and there must be at least
+        one associated value.
+
+    @cvar fullNames: The full names for a directory record.
+        The associated values must be L{unicode}s.
+
+    @cvar emailAddresses: The email addresses for a directory record.
+        The associated values must be L{unicodes}.
+
+    @cvar password: The clear text password for a directory record.
+        The associated value must be a L{unicode} or C{None}.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     uid            = NamedConstant()
</span><span class="cx">     guid           = NamedConstant()
</span><span class="lines">@@ -119,17 +160,46 @@
</span><span class="cx">     emailAddresses.description = &quot;email addresses&quot;
</span><span class="cx">     password.description       = &quot;password&quot;
</span><span class="cx"> 
</span><ins>+    guid.valueType = UUID
+
</ins><span class="cx">     shortNames.multiValue     = True
</span><span class="cx">     fullNames.multiValue      = True
</span><span class="cx">     emailAddresses.multiValue = True
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @staticmethod
</span><span class="cx">     def isMultiValue(name):
</span><ins>+        &quot;&quot;&quot;
+        Check for whether a field is multi-value (as opposed to single-value).
+
+        @param name: The name of the field.
+        @type name: L{NamedConstant}
+
+        @return: C{True} if the field is multi-value, C{False} otherwise.
+        @rtype: L{BOOL}
+        &quot;&quot;&quot;
</ins><span class="cx">         return getattr(name, &quot;multiValue&quot;, False)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @staticmethod
+    def valueType(name):
+        &quot;&quot;&quot;
+        Check for the expected type of values for a field.
</ins><span class="cx"> 
</span><ins>+        @param name: The name of the field.
+        @type name: L{NamedConstant}
+
+        @return: The expected type.
+        @rtype: L{type}
+        &quot;&quot;&quot;
+        return getattr(name, &quot;valueType&quot;, unicode)
+
+
+
</ins><span class="cx"> class Operand(Names):
</span><ins>+    &quot;&quot;&quot;
+    Contants for common operands.
+    &quot;&quot;&quot;
</ins><span class="cx">     OR  = NamedConstant()
</span><span class="cx">     AND = NamedConstant()
</span><span class="cx"> 
</span><span class="lines">@@ -138,9 +208,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-##
</del><ins>+#
</ins><span class="cx"> # Interfaces
</span><del>-##
</del><ins>+#
</ins><span class="cx"> 
</span><span class="cx"> class IDirectoryService(Interface):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -156,109 +226,172 @@
</span><span class="cx"> 
</span><span class="cx">     A directory service may allow support the editing, removal and
</span><span class="cx">     addition of records.
</span><ins>+    Services are read-only should fail with L{NotAllowedError} in editing
+    methods.
+
+    The L{FieldName.uid} field, the L{FieldName.guid} field (if not C{None}),
+    and the combination of the L{FieldName.recordType} and
+    L{FieldName.shortName} fields must be unique to each directory record
+    vended by a directory service.
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    realmName = Attribute(&quot;The name of the authentication realm this service represents.&quot;)
</del><span class="cx"> 
</span><ins>+    realmName = Attribute(
+        &quot;The name of the authentication realm this service represents.&quot;
+    )
+
+
</ins><span class="cx">     def recordTypes():
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        @return: an iterable of L{NamedConstant}s denoting the record
-            types that are kept in this directory.
</del><ins>+        Get the record types supported by this directory service.
+
+        @return: The record types that are supported by this directory service.
+        @rtype: iterable of L{NamedConstant}s
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordsFromExpression(self, expression):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find records matching an expression.
</span><ins>+
</ins><span class="cx">         @param expression: an expression to apply
</span><span class="cx">         @type expression: L{object}
</span><del>-        @return: a deferred iterable of matching L{IDirectoryRecord}s.
</del><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
+
</ins><span class="cx">         @raises: L{QueryNotSupportedError} if the expression is not
</span><span class="cx">             supported by this directory service.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordsFromQuery(expressions, operand=Operand.AND):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find records by composing a query consisting of an iterable of
</span><span class="cx">         expressions and an operand.
</span><ins>+
</ins><span class="cx">         @param expressions: expressions to query against
</span><span class="cx">         @type expressions: iterable of L{object}s
</span><ins>+
</ins><span class="cx">         @param operand: an operand
</span><span class="cx">         @type operand: a L{NamedConstant}
</span><del>-        @return: a deferred iterable of matching L{IDirectoryRecord}s.
</del><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
+
</ins><span class="cx">         @raises: L{QueryNotSupportedError} if the query is not
</span><span class="cx">             supported by this directory service.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordsWithFieldValue(fieldName, value):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find records that have the given field name with the given
</span><span class="cx">         value.
</span><ins>+
</ins><span class="cx">         @param fieldName: a field name
</span><span class="cx">         @type fieldName: L{NamedConstant}
</span><ins>+
</ins><span class="cx">         @param value: a value to match
</span><span class="cx">         @type value: L{bytes}
</span><del>-        @return: a deferred iterable of L{IDirectoryRecord}s.
</del><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordWithUID(uid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the record that has the given UID.
</span><ins>+
</ins><span class="cx">         @param uid: a UID
</span><span class="cx">         @type uid: L{bytes}
</span><del>-        @return: a deferred iterable of L{IDirectoryRecord}s, or
-            C{None} if there is no such record.
</del><ins>+
+        @return: The matching record or C{None} if there is no match.
+        @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-               
</del><ins>+
+
</ins><span class="cx">     def recordWithGUID(guid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the record that has the given GUID.
</span><ins>+
</ins><span class="cx">         @param guid: a GUID
</span><del>-        @type guid: L{bytes}
-        @return: a deferred iterable of L{IDirectoryRecord}s, or
-            C{None} if there is no such record.
</del><ins>+        @type guid: L{UUID}
+
+        @return: The matching record or C{None} if there is no match.
+        @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordsWithRecordType(recordType):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the records that have the given record type.
</span><ins>+
</ins><span class="cx">         @param recordType: a record type
</span><span class="cx">         @type recordType: L{NamedConstant}
</span><del>-        @return: a deferred iterable of L{IDirectoryRecord}s.
</del><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordWithShortName(recordType, shortName):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the record that has the given record type and short name.
</span><ins>+
</ins><span class="cx">         @param recordType: a record type
</span><span class="cx">         @type recordType: L{NamedConstant}
</span><ins>+
</ins><span class="cx">         @param shortName: a short name
</span><span class="cx">         @type shortName: L{bytes}
</span><del>-        @return: a deferred iterable of L{IDirectoryRecord}s, or
-            C{None} if there is no such record.
</del><ins>+
+        @return: The matching record or C{None} if there is no match.
+        @rtype: deferred L{IDirectoryRecord}s or C{None}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordsWithEmailAddress(emailAddress):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the records that have the given email address.
</span><ins>+
</ins><span class="cx">         @param emailAddress: an email address
</span><span class="cx">         @type emailAddress: L{bytes}
</span><del>-        @return: a deferred iterable of L{IDirectoryRecord}s, or
-            C{None} if there is no such record.
</del><ins>+
+        @return: The matching records.
+        @rtype: deferred iterable of L{IDirectoryRecord}s
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def updateRecords(records, create=False):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Updates existing directory records.
</span><ins>+
</ins><span class="cx">         @param records: the records to update
</span><span class="cx">         @type records: iterable of L{IDirectoryRecord}s
</span><ins>+
</ins><span class="cx">         @param create: if true, create records if necessary
</span><span class="cx">         @type create: boolean
</span><ins>+
+        @return: unspecifiied
+        @rtype: deferred object
+
+        @raises L{NotAllowedError}: if the update is not allowed by the
+            directory service.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def removeRecords(uids):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Removes the records with the given UIDs.
</span><ins>+
</ins><span class="cx">         @param uids: the UIDs of the records to remove
</span><span class="cx">         @type uids: iterable of L{bytes}
</span><ins>+
+        @return: unspecifiied
+        @rtype: deferred object
+
+        @raises L{NotAllowedError}: if the removal is not allowed by the
+            directory service.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -294,19 +427,23 @@
</span><span class="cx">     service = Attribute(&quot;The L{IDirectoryService} this record exists in.&quot;)
</span><span class="cx">     fields  = Attribute(&quot;A mapping with L{NamedConstant} keys.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def members():
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the records that are members of this group.  Only direct
</span><span class="cx">         members are included; members of members are not expanded.
</span><ins>+
</ins><span class="cx">         @return: a deferred iterable of L{IDirectoryRecord}s which are
</span><span class="cx">             direct members of this group.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def groups():
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Find the group records that this record is a member of.  Only
</span><span class="cx">         groups for which this record is a direct member is are
</span><span class="cx">         included; membership is not expanded.
</span><ins>+
</ins><span class="cx">         @return: a deferred iterable of L{IDirectoryRecord}s which are
</span><span class="cx">             groups that this record is a member of.
</span><span class="cx">         &quot;&quot;&quot;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoindexpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/index.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/index.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/index.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -29,7 +29,8 @@
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><span class="cx"> 
</span><del>-from twext.who.util import ConstantsContainer, describe, uniqueResult, iterFlags
</del><ins>+from twext.who.util import ConstantsContainer
+from twext.who.util import describe, uniqueResult, iterFlags
</ins><span class="cx"> from twext.who.idirectory import FieldName as BaseFieldName
</span><span class="cx"> from twext.who.expression import MatchExpression, MatchType, MatchFlags
</span><span class="cx"> from twext.who.directory import DirectoryService as BaseDirectoryService
</span><span class="lines">@@ -57,7 +58,10 @@
</span><span class="cx">     XML directory service.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    fieldName = ConstantsContainer(chain(BaseDirectoryService.fieldName.iterconstants(), FieldName.iterconstants()))
</del><ins>+    fieldName = ConstantsContainer(chain(
+        BaseDirectoryService.fieldName.iterconstants(),
+        FieldName.iterconstants()
+    ))
</ins><span class="cx"> 
</span><span class="cx">     indexedFields = (
</span><span class="cx">         BaseFieldName.recordType,
</span><span class="lines">@@ -90,7 +94,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Load records.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        raise NotImplementedError(&quot;Subclasses should implement loadRecords().&quot;)
</del><ins>+        raise NotImplementedError(&quot;Subclasses must implement loadRecords().&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def flush(self):
</span><span class="lines">@@ -112,7 +116,9 @@
</span><span class="cx">                 elif flag == MatchFlags.caseInsensitive:
</span><span class="cx">                     normalize = lambda x: x.lower()
</span><span class="cx">                 else:
</span><del>-                    raise NotImplementedError(&quot;Unknown query flag: %s&quot; % (describe(flag),))
</del><ins>+                    raise NotImplementedError(
+                        &quot;Unknown query flag: {0}&quot;.format(describe(flag))
+                    )
</ins><span class="cx"> 
</span><span class="cx">         return predicate, normalize
</span><span class="cx"> 
</span><span class="lines">@@ -131,16 +137,27 @@
</span><span class="cx">         matchType  = expression.matchType
</span><span class="cx"> 
</span><span class="cx">         if matchType == MatchType.startsWith:
</span><del>-            indexKeys = (key for key in fieldIndex if predicate(normalize(key).startswith(matchValue)))
</del><ins>+            indexKeys = (
+                key for key in fieldIndex
+                if predicate(normalize(key).startswith(matchValue))
+            )
</ins><span class="cx">         elif matchType == MatchType.contains:
</span><del>-            indexKeys = (key for key in fieldIndex if predicate(matchValue in normalize(key)))
</del><ins>+            indexKeys = (
+                key for key in fieldIndex
+                if predicate(matchValue in normalize(key))
+            )
</ins><span class="cx">         elif matchType == MatchType.equals:
</span><span class="cx">             if predicate(True):
</span><span class="cx">                 indexKeys = (matchValue,)
</span><span class="cx">             else:
</span><del>-                indexKeys = (key for key in fieldIndex if normalize(key) != matchValue)
</del><ins>+                indexKeys = (
+                    key for key in fieldIndex
+                    if normalize(key) != matchValue
+                )
</ins><span class="cx">         else:
</span><del>-            raise NotImplementedError(&quot;Unknown match type: %s&quot; % (describe(matchType),))
</del><ins>+            raise NotImplementedError(
+                &quot;Unknown match type: {0}&quot;.format(describe(matchType))
+            )
</ins><span class="cx"> 
</span><span class="cx">         matchingRecords = set()
</span><span class="cx">         for key in indexKeys:
</span><span class="lines">@@ -165,18 +182,25 @@
</span><span class="cx">         matchType  = expression.matchType
</span><span class="cx"> 
</span><span class="cx">         if matchType == MatchType.startsWith:
</span><del>-            match = lambda fieldValue: predicate(fieldValue.startswith(matchValue))
</del><ins>+            match = lambda fieldValue: predicate(
+                fieldValue.startswith(matchValue)
+            )
</ins><span class="cx">         elif matchType == MatchType.contains:
</span><span class="cx">             match = lambda fieldValue: predicate(matchValue in fieldValue)
</span><span class="cx">         elif matchType == MatchType.equals:
</span><span class="cx">             match = lambda fieldValue: predicate(fieldValue == matchValue)
</span><span class="cx">         else:
</span><del>-            raise NotImplementedError(&quot;Unknown match type: %s&quot; % (describe(matchType),))
</del><ins>+            raise NotImplementedError(
+                &quot;Unknown match type: {0}&quot;.format(describe(matchType))
+            )
</ins><span class="cx"> 
</span><span class="cx">         result = set()
</span><span class="cx"> 
</span><span class="cx">         if records is None:
</span><del>-            records = (uniqueResult(values) for values in self.index[self.fieldName.uid].itervalues())
</del><ins>+            records = (
+                uniqueResult(values) for values
+                in self.index[self.fieldName.uid].itervalues()
+            )
</ins><span class="cx"> 
</span><span class="cx">         for record in records:
</span><span class="cx">             fieldValues = record.fields.get(expression.fieldName, None)
</span><span class="lines">@@ -194,11 +218,17 @@
</span><span class="cx">     def recordsFromExpression(self, expression, records=None):
</span><span class="cx">         if isinstance(expression, MatchExpression):
</span><span class="cx">             if expression.fieldName in self.indexedFields:
</span><del>-                return self.indexedRecordsFromMatchExpression(expression, records=records)
</del><ins>+                return self.indexedRecordsFromMatchExpression(
+                    expression, records=records
+                )
</ins><span class="cx">             else:
</span><del>-                return self.unIndexedRecordsFromMatchExpression(expression, records=records)
</del><ins>+                return self.unIndexedRecordsFromMatchExpression(
+                    expression, records=records
+                )
</ins><span class="cx">         else:
</span><del>-            return BaseDirectoryService.recordsFromExpression(self, expression, records=records)
</del><ins>+            return BaseDirectoryService.recordsFromExpression(
+                self, expression, records=records
+            )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -206,6 +236,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     XML directory record
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def members(self):
</span><span class="cx">         members = set()
</span><span class="lines">@@ -215,4 +246,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groups(self):
</span><del>-        return self.service.recordsWithFieldValue(FieldName.memberUIDs, self.uid)
</del><ins>+        return self.service.recordsWithFieldValue(
+            FieldName.memberUIDs, self.uid
+        )
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhotesttest_aggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/test/test_aggregate.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/test/test_aggregate.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/test/test_aggregate.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -26,7 +26,8 @@
</span><span class="cx"> from twext.who.util import ConstantsContainer
</span><span class="cx"> 
</span><span class="cx"> from twext.who.test import test_directory, test_xml
</span><del>-from twext.who.test.test_xml import QueryMixIn, xmlService, TestService as XMLTestService
</del><ins>+from twext.who.test.test_xml import QueryMixIn, xmlService
+from twext.who.test.test_xml import TestService as XMLTestService
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -68,7 +69,10 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceImmutableTest(BaseTest, test_directory.DirectoryServiceImmutableTest):
</del><ins>+class DirectoryServiceImmutableTest(
+    BaseTest,
+    test_directory.BaseDirectoryServiceImmutableTest,
+):
</ins><span class="cx">     pass
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -81,24 +85,39 @@
</span><span class="cx">         class GroupsDirectoryService(XMLTestService):
</span><span class="cx">             recordType = ConstantsContainer((XMLTestService.recordType.group,))
</span><span class="cx"> 
</span><del>-        usersService  = self.xmlService(testXMLConfigUsers, UsersDirectoryService)
-        groupsService = self.xmlService(testXMLConfigGroups, GroupsDirectoryService)
</del><ins>+        usersService = self.xmlService(
+            testXMLConfigUsers,
+            UsersDirectoryService
+        )
+        groupsService = self.xmlService(
+            testXMLConfigGroups,
+            GroupsDirectoryService
+        )
</ins><span class="cx"> 
</span><span class="cx">         return BaseTest.service(self, (usersService, groupsService))
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceAggregatedBaseTest(AggregatedBaseTest, DirectoryServiceBaseTest):
</del><ins>+class DirectoryServiceAggregatedBaseTest(
+    AggregatedBaseTest,
+    DirectoryServiceBaseTest,
+):
</ins><span class="cx">     pass
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceAggregatedQueryTest(AggregatedBaseTest, test_xml.DirectoryServiceQueryTest):
</del><ins>+class DirectoryServiceAggregatedQueryTest(
+    AggregatedBaseTest,
+    test_xml.DirectoryServiceQueryTest,
+):
</ins><span class="cx">     pass
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceAggregatedImmutableTest(AggregatedBaseTest, test_directory.DirectoryServiceImmutableTest):
</del><ins>+class DirectoryServiceAggregatedImmutableTest(
+    AggregatedBaseTest,
+    test_directory.BaseDirectoryServiceImmutableTest,
+):
</ins><span class="cx">     pass
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhotesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/test/test_directory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/test/test_directory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/test/test_directory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class BaseTest(unittest.TestCase):
</del><ins>+class ServiceMixIn(object):
</ins><span class="cx">     realmName = &quot;xyzzy&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceTest(BaseTest):
</del><ins>+class BaseDirectoryServiceTest(ServiceMixIn):
</ins><span class="cx">     def test_interface(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         try:
</span><span class="lines">@@ -79,7 +79,10 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsFromQueryBogus(self):
</span><span class="cx">         service = self.service()
</span><del>-        self.assertFailure(service.recordsFromQuery((object(),)), QueryNotSupportedError)
</del><ins>+        self.assertFailure(
+            service.recordsFromQuery((object(),)),
+            QueryNotSupportedError
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_recordWithUID(self):
</span><span class="lines">@@ -103,13 +106,61 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceImmutableTest(BaseTest):
</del><ins>+class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
+    def test_recordsFromExpression(self):
+        service = self.service()
+        result = yield(service.recordsFromExpression(None))
+        self.assertFailure(result, QueryNotSupportedError)
+
+
+    def test_recordWithUID(self):
+        service = self.service()
+        self.assertFailure(
+            service.recordWithUID(None),
+            QueryNotSupportedError
+        )
+
+
+    def test_recordWithGUID(self):
+        service = self.service()
+        self.assertFailure(
+            service.recordWithGUID(None),
+            QueryNotSupportedError
+        )
+
+
+    def test_recordsWithRecordType(self):
+        service = self.service()
+        self.assertFailure(
+            service.recordsWithRecordType(None),
+            QueryNotSupportedError
+        )
+
+
+    def test_recordWithShortName(self):
+        service = self.service()
+        self.assertFailure(
+            service.recordWithShortName(None, None),
+            QueryNotSupportedError
+        )
+
+
+    def test_recordsWithEmailAddress(self):
+        service = self.service()
+        self.assertFailure(
+            service.recordsWithEmailAddress(None),
+            QueryNotSupportedError
+        )
+
+
+
+class BaseDirectoryServiceImmutableTest(ServiceMixIn):
</ins><span class="cx">     def test_updateRecordsNotAllowed(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="cx">         newRecord = DirectoryRecord(
</span><span class="cx">             service,
</span><del>-            fields = {
</del><ins>+            fields={
</ins><span class="cx">                 service.fieldName.uid:        &quot;__plugh__&quot;,
</span><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><span class="cx">                 service.fieldName.shortNames: (&quot;plugh&quot;,),
</span><span class="lines">@@ -138,33 +189,55 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryRecordTest(BaseTest):
</del><ins>+class DirectoryServiceImmutableTest(
+    unittest.TestCase,
+    BaseDirectoryServiceImmutableTest,
+):
+    pass
+
+
+
+class BaseDirectoryRecordTest(ServiceMixIn):
</ins><span class="cx">     fields_wsanchez = {
</span><del>-        FieldName.uid:            &quot;UID:wsanchez&quot;,
-        FieldName.recordType:     RecordType.user,
-        FieldName.shortNames:     (&quot;wsanchez&quot;, &quot;wilfredo_sanchez&quot;),
-        FieldName.fullNames:      (&quot;Wilfredo Sanchez&quot;, &quot;Wilfredo Sanchez Vega&quot;),
-        FieldName.emailAddresses: (&quot;wsanchez@calendarserver.org&quot;, &quot;wsanchez@example.com&quot;)
</del><ins>+        FieldName.uid: &quot;UID:wsanchez&quot;,
+        FieldName.recordType: RecordType.user,
+        FieldName.shortNames: (&quot;wsanchez&quot;, &quot;wilfredo_sanchez&quot;),
+        FieldName.fullNames: (
+            &quot;Wilfredo Sanchez&quot;,
+            &quot;Wilfredo Sanchez Vega&quot;,
+        ),
+        FieldName.emailAddresses: (
+            &quot;wsanchez@calendarserver.org&quot;,
+            &quot;wsanchez@example.com&quot;,
+        )
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     fields_glyph = {
</span><del>-        FieldName.uid:            &quot;UID:glyph&quot;,
-        FieldName.recordType:     RecordType.user,
-        FieldName.shortNames:     (&quot;glyph&quot;,),
-        FieldName.fullNames:      (&quot;Glyph Lefkowitz&quot;,),
</del><ins>+        FieldName.uid: &quot;UID:glyph&quot;,
+        FieldName.recordType: RecordType.user,
+        FieldName.shortNames: (&quot;glyph&quot;,),
+        FieldName.fullNames: (&quot;Glyph Lefkowitz&quot;,),
</ins><span class="cx">         FieldName.emailAddresses: (&quot;glyph@calendarserver.org&quot;,)
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     fields_sagen = {
</span><del>-        FieldName.uid:            &quot;UID:sagen&quot;,
-        FieldName.recordType:     RecordType.user,
-        FieldName.shortNames:     (&quot;sagen&quot;,),
-        FieldName.fullNames:      (&quot;Morgen Sagen&quot;,),
</del><ins>+        FieldName.uid: &quot;UID:sagen&quot;,
+        FieldName.recordType: RecordType.user,
+        FieldName.shortNames: (&quot;sagen&quot;,),
+        FieldName.fullNames: (&quot;Morgen Sagen&quot;,),
</ins><span class="cx">         FieldName.emailAddresses: (&quot;sagen@CalendarServer.org&quot;,)
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    fields_staff = {
+        FieldName.uid: &quot;UID:staff&quot;,
+        FieldName.recordType: RecordType.group,
+        FieldName.shortNames: (&quot;staff&quot;,),
+        FieldName.fullNames: (&quot;Staff&quot;,),
+        FieldName.emailAddresses: (&quot;staff@CalendarServer.org&quot;,)
+    }
</ins><span class="cx"> 
</span><del>-    def _testRecord(self, fields=None, service=None):
</del><ins>+
+    def makeRecord(self, fields=None, service=None):
</ins><span class="cx">         if fields is None:
</span><span class="cx">             fields = self.fields_wsanchez
</span><span class="cx">         if service is None:
</span><span class="lines">@@ -173,7 +246,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_interface(self):
</span><del>-        record = self._testRecord()
</del><ins>+        record = self.makeRecord()
</ins><span class="cx">         try:
</span><span class="cx">             verifyObject(IDirectoryRecord, record)
</span><span class="cx">         except BrokenMethodImplementation as e:
</span><span class="lines">@@ -182,58 +255,58 @@
</span><span class="cx"> 
</span><span class="cx">     def test_init(self):
</span><span class="cx">         service  = self.service()
</span><del>-        wsanchez = self._testRecord(self.fields_wsanchez, service=service)
</del><ins>+        wsanchez = self.makeRecord(self.fields_wsanchez, service=service)
</ins><span class="cx"> 
</span><span class="cx">         self.assertEquals(wsanchez.service, service)
</span><del>-        self.assertEquals(wsanchez.fields , self.fields_wsanchez)
</del><ins>+        self.assertEquals(wsanchez.fields, self.fields_wsanchez)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_initWithNoUID(self):
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         del fields[FieldName.uid]
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.uid] = &quot;&quot;
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_initWithNoRecordType(self):
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         del fields[FieldName.recordType]
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.recordType] = &quot;&quot;
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_initWithNoShortNames(self):
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         del fields[FieldName.shortNames]
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.shortNames] = ()
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.shortNames] = (&quot;&quot;,)
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.shortNames] = (&quot;wsanchez&quot;, &quot;&quot;)
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_initWithBogusRecordType(self):
</span><span class="cx">         fields = self.fields_wsanchez.copy()
</span><span class="cx">         fields[FieldName.recordType] = object()
</span><del>-        self.assertRaises(ValueError, self._testRecord, fields)
</del><ins>+        self.assertRaises(ValueError, self.makeRecord, fields)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_initNormalize(self):
</span><del>-        sagen = self._testRecord(self.fields_sagen)
</del><ins>+        sagen = self.makeRecord(self.fields_sagen)
</ins><span class="cx"> 
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             sagen.fields[FieldName.emailAddresses],
</span><span class="lines">@@ -245,30 +318,44 @@
</span><span class="cx">         fields_glyphmod = self.fields_glyph.copy()
</span><span class="cx">         del fields_glyphmod[FieldName.emailAddresses]
</span><span class="cx"> 
</span><del>-        wsanchez    = self._testRecord(self.fields_wsanchez)
-        wsanchezmod = self._testRecord(self.fields_wsanchez, DirectoryService(&quot;plugh&quot;))
-        glyph       = self._testRecord(self.fields_glyph)
-        glyphmod    = self._testRecord(fields_glyphmod)
</del><ins>+        plugh = DirectoryService(&quot;plugh&quot;)
</ins><span class="cx"> 
</span><ins>+        wsanchez    = self.makeRecord(self.fields_wsanchez)
+        wsanchezmod = self.makeRecord(self.fields_wsanchez, plugh)
+        glyph       = self.makeRecord(self.fields_glyph)
+        glyphmod    = self.makeRecord(fields_glyphmod)
+
</ins><span class="cx">         self.assertEquals(wsanchez, wsanchez)
</span><span class="cx">         self.assertNotEqual(wsanchez, glyph)
</span><del>-        self.assertNotEqual(glyph, glyphmod) # UID matches, other fields do not
</del><ins>+        self.assertNotEqual(glyph, glyphmod)  # UID matches, other fields don't
</ins><span class="cx">         self.assertNotEqual(glyphmod, wsanchez)
</span><del>-        self.assertNotEqual(wsanchez, wsanchezmod) # Different service
</del><ins>+        self.assertNotEqual(wsanchez, wsanchezmod)  # Different service
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_attributeAccess(self):
</span><del>-        wsanchez = self._testRecord(self.fields_wsanchez)
</del><ins>+        wsanchez = self.makeRecord(self.fields_wsanchez)
</ins><span class="cx"> 
</span><del>-        self.assertEquals(wsanchez.recordType    , wsanchez.fields[FieldName.recordType    ])
-        self.assertEquals(wsanchez.uid           , wsanchez.fields[FieldName.uid           ])
-        self.assertEquals(wsanchez.shortNames    , wsanchez.fields[FieldName.shortNames    ])
-        self.assertEquals(wsanchez.emailAddresses, wsanchez.fields[FieldName.emailAddresses])
</del><ins>+        self.assertEquals(
+            wsanchez.recordType,
+            wsanchez.fields[FieldName.recordType]
+        )
+        self.assertEquals(
+            wsanchez.uid,
+            wsanchez.fields[FieldName.uid]
+        )
+        self.assertEquals(
+            wsanchez.shortNames,
+            wsanchez.fields[FieldName.shortNames]
+        )
+        self.assertEquals(
+            wsanchez.emailAddresses,
+            wsanchez.fields[FieldName.emailAddresses]
+        )
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_members(self):
</span><del>-        wsanchez = self._testRecord(self.fields_wsanchez)
-
</del><ins>+        wsanchez = self.makeRecord(self.fields_wsanchez)
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set((yield wsanchez.members())),
</span><span class="cx">             set()
</span><span class="lines">@@ -276,5 +363,24 @@
</span><span class="cx"> 
</span><span class="cx">         raise SkipTest(&quot;Subclasses should implement this test.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def test_groups(self):
</span><span class="cx">         raise SkipTest(&quot;Subclasses should implement this test.&quot;)
</span><ins>+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+    def test_members(self):
+        wsanchez = self.makeRecord(self.fields_wsanchez)
+        self.assertEquals(
+            set((yield wsanchez.members())),
+            set()
+        )
+
+        staff = self.makeRecord(self.fields_staff)
+        self.assertFailure(staff.members(), NotImplementedError)
+
+
+    def test_groups(self):
+        wsanchez = self.makeRecord(self.fields_wsanchez)
+        self.assertFailure(wsanchez.groups(), NotImplementedError)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhotesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/test/test_util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/test/test_util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/test/test_util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -82,7 +82,11 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">     def test_lookupByName(self):
</span><del>-        constants = set((Instruments.hammer, Tools.screwdriver, Instruments.chisel))
</del><ins>+        constants = set((
+            Instruments.hammer,
+            Tools.screwdriver,
+            Instruments.chisel,
+        ))
</ins><span class="cx">         container = ConstantsContainer(constants)
</span><span class="cx"> 
</span><span class="cx">         self.assertEquals(
</span><span class="lines">@@ -108,7 +112,7 @@
</span><span class="cx"> class UtilTest(unittest.TestCase):
</span><span class="cx">     def test_uniqueResult(self):
</span><span class="cx">         self.assertEquals(1, uniqueResult((1,)))
</span><del>-        self.assertRaises(DirectoryServiceError, uniqueResult, (1,2,3))
</del><ins>+        self.assertRaises(DirectoryServiceError, uniqueResult, (1, 2, 3))
</ins><span class="cx"> 
</span><span class="cx">     def test_describe(self):
</span><span class="cx">         self.assertEquals(&quot;nail pounder&quot;, describe(Tools.hammer))
</span><span class="lines">@@ -116,5 +120,5 @@
</span><span class="cx"> 
</span><span class="cx">     def test_describeFlags(self):
</span><span class="cx">         self.assertEquals(&quot;blue&quot;, describe(Switches.b))
</span><del>-        self.assertEquals(&quot;red|green&quot;, describe(Switches.r|Switches.g))
-        self.assertEquals(&quot;blue|black&quot;, describe(Switches.b|Switches.black))
</del><ins>+        self.assertEquals(&quot;red|green&quot;, describe(Switches.r | Switches.g))
+        self.assertEquals(&quot;blue|black&quot;, describe(Switches.b | Switches.black))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhotesttest_xmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/test/test_xml.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/test/test_xml.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/test/test_xml.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -19,6 +19,7 @@
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> from time import sleep
</span><ins>+from uuid import UUID
</ins><span class="cx"> 
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx"> from twisted.python.filepath import FilePath
</span><span class="lines">@@ -47,7 +48,10 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceBaseTest(BaseTest, test_directory.DirectoryServiceTest):
</del><ins>+class DirectoryServiceBaseTest(
+    BaseTest,
+    test_directory.BaseDirectoryServiceTest,
+):
</ins><span class="cx">     def test_repr(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -60,17 +64,21 @@
</span><span class="cx">     def test_recordWithUID(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__null__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__null__&quot;))
</ins><span class="cx">         self.assertEquals(record, None)
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
-        self.assertEquals(record.uid, &quot;__wsanchez__&quot;)
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
+        self.assertEquals(record.uid, u&quot;__wsanchez__&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordWithGUID(self):
</span><span class="cx">         service = self.service()
</span><del>-        record = (yield service.recordWithGUID(&quot;6C495FCD-7E78-4D5C-AA66-BC890AD04C9D&quot;))
</del><ins>+        record = (
+            yield service.recordWithGUID(
+                UUID(&quot;6C495FCD-7E78-4D5C-AA66-BC890AD04C9D&quot;)
+            )
+        )
</ins><span class="cx">         self.assertEquals(record, None)
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -80,27 +88,33 @@
</span><span class="cx">         records = (yield service.recordsWithRecordType(object()))
</span><span class="cx">         self.assertEquals(set(records), set())
</span><span class="cx"> 
</span><del>-        records = (yield service.recordsWithRecordType(service.recordType.user))
-        self.assertRecords(records,
</del><ins>+        records = (
+            yield service.recordsWithRecordType(service.recordType.user)
+        )
+        self.assertRecords(
+            records,
</ins><span class="cx">             (
</span><del>-                &quot;__wsanchez__&quot;,
-                &quot;__glyph__&quot;,
-                &quot;__sagen__&quot;,
-                &quot;__cdaboo__&quot;,
-                &quot;__dre__&quot;,
-                &quot;__exarkun__&quot;,
-                &quot;__dreid__&quot;,
-                &quot;__alyssa__&quot;,
-                &quot;__joe__&quot;,
</del><ins>+                u&quot;__wsanchez__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__alyssa__&quot;,
+                u&quot;__joe__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        records = (yield service.recordsWithRecordType(service.recordType.group))
-        self.assertRecords(records,
</del><ins>+        records = (
+            yield service.recordsWithRecordType(service.recordType.group)
+        )
+        self.assertRecords(
+            records,
</ins><span class="cx">             (
</span><del>-                &quot;__calendar-dev__&quot;,
-                &quot;__twisted__&quot;,
-                &quot;__developers__&quot;,
</del><ins>+                u&quot;__calendar-dev__&quot;,
+                u&quot;__twisted__&quot;,
+                u&quot;__developers__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -109,28 +123,55 @@
</span><span class="cx">     def test_recordWithShortName(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithShortName(service.recordType.user, &quot;null&quot;))
</del><ins>+        record = (
+            yield service.recordWithShortName(
+                service.recordType.user,
+                u&quot;null&quot;,
+            )
+        )
</ins><span class="cx">         self.assertEquals(record, None)
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithShortName(service.recordType.user, &quot;wsanchez&quot;))
-        self.assertEquals(record.uid, &quot;__wsanchez__&quot;)
</del><ins>+        record = (
+            yield service.recordWithShortName(
+                service.recordType.user,
+                u&quot;wsanchez&quot;,
+            )
+        )
+        self.assertEquals(record.uid, u&quot;__wsanchez__&quot;)
</ins><span class="cx"> 
</span><del>-        record = (yield service.recordWithShortName(service.recordType.user, &quot;wilfredo_sanchez&quot;))
-        self.assertEquals(record.uid, &quot;__wsanchez__&quot;)
</del><ins>+        record = (
+            yield service.recordWithShortName(
+                service.recordType.user,
+                u&quot;wilfredo_sanchez&quot;,
+            )
+        )
+        self.assertEquals(record.uid, u&quot;__wsanchez__&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsWithEmailAddress(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        records = (yield service.recordsWithEmailAddress(&quot;wsanchez@bitbucket.calendarserver.org&quot;))
-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        records = (
+            yield service.recordsWithEmailAddress(
+                u&quot;wsanchez@bitbucket.calendarserver.org&quot;
+            )
+        )
+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><del>-        records = (yield service.recordsWithEmailAddress(&quot;wsanchez@devnull.twistedmatrix.com&quot;))
-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        records = (
+            yield service.recordsWithEmailAddress(
+                u&quot;wsanchez@devnull.twistedmatrix.com&quot;
+            )
+        )
+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><del>-        records = (yield service.recordsWithEmailAddress(&quot;shared@example.com&quot;))
-        self.assertRecords(records, (&quot;__sagen__&quot;, &quot;__dre__&quot;))
</del><ins>+        records = (
+            yield service.recordsWithEmailAddress(
+                u&quot;shared@example.com&quot;
+            )
+        )
+        self.assertRecords(records, (u&quot;__sagen__&quot;, u&quot;__dre__&quot;))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -138,7 +179,7 @@
</span><span class="cx">     def test_realmNameImmutable(self):
</span><span class="cx">         def setRealmName():
</span><span class="cx">             service = self.service()
</span><del>-            service.realmName = &quot;foo&quot;
</del><ins>+            service.realmName = u&quot;foo&quot;
</ins><span class="cx"> 
</span><span class="cx">         self.assertRaises(AssertionError, setRealmName)
</span><span class="cx"> 
</span><span class="lines">@@ -176,12 +217,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_badRootElement(self):
</span><del>-        service = self.service(xmlData=
-&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</del><ins>+        service = self.service(xmlData=(
+b&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;frobnitz /&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><del>-        )
</del><ins>+        ))
</ins><span class="cx"> 
</span><span class="cx">         self.assertRaises(ParseError, service.loadRecords)
</span><span class="cx">         try:
</span><span class="lines">@@ -193,12 +234,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_noRealmName(self):
</span><del>-        service = self.service(xmlData=
-&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</del><ins>+        service = self.service(xmlData=(
+b&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;directory /&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><del>-        )
</del><ins>+        ))
</ins><span class="cx"> 
</span><span class="cx">         self.assertRaises(ParseError, service.loadRecords)
</span><span class="cx">         try:
</span><span class="lines">@@ -215,8 +256,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_unknownFieldElementsDirty(self):
</span><del>-        service = self.service(xmlData=
-&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</del><ins>+        service = self.service(xmlData=(
+b&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;directory realm=&quot;Unknown Record Types&quot;&gt;
</span><span class="cx">   &lt;record type=&quot;user&quot;&gt;
</span><span class="lines">@@ -226,8 +267,11 @@
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx"> &lt;/directory&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><ins>+        ))
+        self.assertEquals(
+            set(service.unknownFieldElements),
+            set((u&quot;political-affiliation&quot;,))
</ins><span class="cx">         )
</span><del>-        self.assertEquals(set(service.unknownFieldElements), set((&quot;political-affiliation&quot;,)))
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_unknownRecordTypesClean(self):
</span><span class="lines">@@ -236,8 +280,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_unknownRecordTypesDirty(self):
</span><del>-        service = self.service(xmlData=
-&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</del><ins>+        service = self.service(xmlData=(
+b&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;directory realm=&quot;Unknown Record Types&quot;&gt;
</span><span class="cx">   &lt;record type=&quot;camera&quot;&gt;
</span><span class="lines">@@ -247,8 +291,8 @@
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx"> &lt;/directory&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><del>-        )
-        self.assertEquals(set(service.unknownRecordTypes), set((&quot;camera&quot;,)))
</del><ins>+        ))
+        self.assertEquals(set(service.unknownRecordTypes), set((u&quot;camera&quot;,)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -258,12 +302,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery(
</span><span class="cx">             (
</span><del>-                service.query(&quot;emailAddresses&quot;, &quot;shared@example.com&quot;),
-                service.query(&quot;shortNames&quot;, &quot;sagen&quot;),
</del><ins>+                service.query(u&quot;emailAddresses&quot;, u&quot;shared@example.com&quot;),
+                service.query(u&quot;shortNames&quot;, u&quot;sagen&quot;),
</ins><span class="cx">             ),
</span><span class="cx">             operand=Operand.AND
</span><span class="cx">         )
</span><del>-        self.assertRecords(records, (&quot;__sagen__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__sagen__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -274,8 +318,8 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery(
</span><span class="cx">             (
</span><del>-                service.query(&quot;emailAddresses&quot;, &quot;nobody@example.com&quot;),
-                service.query(&quot;shortNames&quot;, &quot;sagen&quot;),
</del><ins>+                service.query(u&quot;emailAddresses&quot;, u&quot;nobody@example.com&quot;),
+                service.query(u&quot;shortNames&quot;, u&quot;sagen&quot;),
</ins><span class="cx">             ),
</span><span class="cx">             operand=Operand.AND
</span><span class="cx">         )
</span><span class="lines">@@ -287,12 +331,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery(
</span><span class="cx">             (
</span><del>-                service.query(&quot;emailAddresses&quot;, &quot;shared@example.com&quot;),
-                service.query(&quot;shortNames&quot;, &quot;wsanchez&quot;),
</del><ins>+                service.query(u&quot;emailAddresses&quot;, u&quot;shared@example.com&quot;),
+                service.query(u&quot;shortNames&quot;, u&quot;wsanchez&quot;),
</ins><span class="cx">             ),
</span><span class="cx">             operand=Operand.OR
</span><span class="cx">         )
</span><del>-        self.assertRecords(records, (&quot;__sagen__&quot;, &quot;__dre__&quot;, &quot;__wsanchez__&quot;))
</del><ins>+        self.assertRecords(records, (u&quot;__sagen__&quot;, u&quot;__dre__&quot;, u&quot;__wsanchez__&quot;))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -300,12 +344,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery(
</span><span class="cx">             (
</span><del>-                service.query(&quot;emailAddresses&quot;, &quot;shared@example.com&quot;),
-                service.query(&quot;shortNames&quot;, &quot;sagen&quot;, flags=MatchFlags.NOT),
</del><ins>+                service.query(u&quot;emailAddresses&quot;, u&quot;shared@example.com&quot;),
+                service.query(u&quot;shortNames&quot;, u&quot;sagen&quot;, flags=MatchFlags.NOT),
</ins><span class="cx">             ),
</span><span class="cx">             operand=Operand.AND
</span><span class="cx">         )
</span><del>-        self.assertRecords(records, (&quot;__dre__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__dre__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -313,48 +357,60 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery(
</span><span class="cx">             (
</span><del>-                service.query(&quot;emailAddresses&quot;, &quot;shared@example.com&quot;),
-                service.query(&quot;fullNames&quot;, &quot;Andre LaBranche&quot;, flags=MatchFlags.NOT),
</del><ins>+                service.query(u&quot;emailAddresses&quot;, u&quot;shared@example.com&quot;),
+                service.query(
+                    u&quot;fullNames&quot;, u&quot;Andre LaBranche&quot;,
+                    flags=MatchFlags.NOT
+                ),
</ins><span class="cx">             ),
</span><span class="cx">             operand=Operand.AND
</span><span class="cx">         )
</span><del>-        self.assertRecords(records, (&quot;__sagen__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__sagen__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryCaseInsensitive(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;shortNames&quot;, &quot;SagEn&quot;, flags=MatchFlags.caseInsensitive),
</del><ins>+            service.query(
+                u&quot;shortNames&quot;, u&quot;SagEn&quot;,
+                flags=MatchFlags.caseInsensitive
+            ),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__sagen__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__sagen__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryCaseInsensitiveNoIndex(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;fullNames&quot;, &quot;moRGen SAGen&quot;, flags=MatchFlags.caseInsensitive),
</del><ins>+            service.query(
+                u&quot;fullNames&quot;, u&quot;moRGen SAGen&quot;,
+                flags=MatchFlags.caseInsensitive
+            ),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__sagen__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__sagen__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryStartsWith(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;shortNames&quot;, &quot;wil&quot;, matchType=MatchType.startsWith),
</del><ins>+            service.query(u&quot;shortNames&quot;, u&quot;wil&quot;, matchType=MatchType.startsWith),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryStartsWithNoIndex(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;fullNames&quot;, &quot;Wilfredo&quot;, matchType=MatchType.startsWith),
</del><ins>+            service.query(
+                u&quot;fullNames&quot;, u&quot;Wilfredo&quot;,
+                matchType=MatchType.startsWith
+            ),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -362,25 +418,25 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;shortNames&quot;, &quot;w&quot;,
-                matchType = MatchType.startsWith,
-                flags = MatchFlags.NOT,
</del><ins>+                u&quot;shortNames&quot;, u&quot;w&quot;,
+                matchType=MatchType.startsWith,
+                flags=MatchFlags.NOT,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx">         self.assertRecords(
</span><span class="cx">             records,
</span><span class="cx">             (
</span><del>-                '__alyssa__',
-                '__calendar-dev__',
-                '__cdaboo__',
-                '__developers__',
-                '__dre__',
-                '__dreid__',
-                '__exarkun__',
-                '__glyph__',
-                '__joe__',
-                '__sagen__',
-                '__twisted__',
</del><ins>+                u&quot;__alyssa__&quot;,
+                u&quot;__calendar-dev__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__developers__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__joe__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__twisted__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -396,26 +452,26 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;shortNames&quot;, &quot;wil&quot;,
-                matchType = MatchType.startsWith,
-                flags = MatchFlags.NOT,
</del><ins>+                u&quot;shortNames&quot;, u&quot;wil&quot;,
+                matchType=MatchType.startsWith,
+                flags=MatchFlags.NOT,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx">         self.assertRecords(
</span><span class="cx">             records,
</span><span class="cx">             (
</span><del>-                '__alyssa__',
-                '__calendar-dev__',
-                '__cdaboo__',
-                '__developers__',
-                '__dre__',
-                '__dreid__',
-                '__exarkun__',
-                '__glyph__',
-                '__joe__',
-                '__sagen__',
-                '__twisted__',
-                '__wsanchez__',
</del><ins>+                u&quot;__alyssa__&quot;,
+                u&quot;__calendar-dev__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__developers__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__joe__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__twisted__&quot;,
+                u&quot;__wsanchez__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -425,25 +481,25 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;fullNames&quot;, &quot;Wilfredo&quot;,
-                matchType = MatchType.startsWith,
-                flags = MatchFlags.NOT,
</del><ins>+                u&quot;fullNames&quot;, u&quot;Wilfredo&quot;,
+                matchType=MatchType.startsWith,
+                flags=MatchFlags.NOT,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx">         self.assertRecords(
</span><span class="cx">             records,
</span><span class="cx">             (
</span><del>-                '__alyssa__',
-                '__calendar-dev__',
-                '__cdaboo__',
-                '__developers__',
-                '__dre__',
-                '__dreid__',
-                '__exarkun__',
-                '__glyph__',
-                '__joe__',
-                '__sagen__',
-                '__twisted__',
</del><ins>+                u&quot;__alyssa__&quot;,
+                u&quot;__calendar-dev__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__developers__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__joe__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__twisted__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -453,12 +509,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;shortNames&quot;, &quot;WIL&quot;,
-                matchType = MatchType.startsWith,
-                flags = MatchFlags.caseInsensitive,
</del><ins>+                u&quot;shortNames&quot;, u&quot;WIL&quot;,
+                matchType=MatchType.startsWith,
+                flags=MatchFlags.caseInsensitive,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -466,30 +522,33 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;fullNames&quot;, &quot;wilfrEdo&quot;,
-                matchType = MatchType.startsWith,
-                flags = MatchFlags.caseInsensitive,
</del><ins>+                u&quot;fullNames&quot;, u&quot;wilfrEdo&quot;,
+                matchType=MatchType.startsWith,
+                flags=MatchFlags.caseInsensitive,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryContains(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;shortNames&quot;, &quot;sanchez&quot;, matchType=MatchType.contains),
</del><ins>+            service.query(
+                u&quot;shortNames&quot;, u&quot;sanchez&quot;,
+                matchType=MatchType.contains
+            ),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryContainsNoIndex(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><del>-            service.query(&quot;fullNames&quot;, &quot;fred&quot;, matchType=MatchType.contains),
</del><ins>+            service.query(u&quot;fullNames&quot;, u&quot;fred&quot;, matchType=MatchType.contains),
</ins><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -497,25 +556,25 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;shortNames&quot;, &quot;sanchez&quot;,
-                matchType = MatchType.contains,
-                flags = MatchFlags.NOT,
</del><ins>+                u&quot;shortNames&quot;, u&quot;sanchez&quot;,
+                matchType=MatchType.contains,
+                flags=MatchFlags.NOT,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx">         self.assertRecords(
</span><span class="cx">             records,
</span><span class="cx">             (
</span><del>-                '__alyssa__',
-                '__calendar-dev__',
-                '__cdaboo__',
-                '__developers__',
-                '__dre__',
-                '__dreid__',
-                '__exarkun__',
-                '__glyph__',
-                '__joe__',
-                '__sagen__',
-                '__twisted__',
</del><ins>+                u&quot;__alyssa__&quot;,
+                u&quot;__calendar-dev__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__developers__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__joe__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__twisted__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -525,25 +584,25 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;fullNames&quot;, &quot;fred&quot;,
-                matchType = MatchType.contains,
-                flags = MatchFlags.NOT,
</del><ins>+                u&quot;fullNames&quot;, u&quot;fred&quot;,
+                matchType=MatchType.contains,
+                flags=MatchFlags.NOT,
</ins><span class="cx">             ),
</span><span class="cx">         ))
</span><span class="cx">         self.assertRecords(
</span><span class="cx">             records,
</span><span class="cx">             (
</span><del>-                '__alyssa__',
-                '__calendar-dev__',
-                '__cdaboo__',
-                '__developers__',
-                '__dre__',
-                '__dreid__',
-                '__exarkun__',
-                '__glyph__',
-                '__joe__',
-                '__sagen__',
-                '__twisted__',
</del><ins>+                u&quot;__alyssa__&quot;,
+                u&quot;__calendar-dev__&quot;,
+                u&quot;__cdaboo__&quot;,
+                u&quot;__developers__&quot;,
+                u&quot;__dre__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__joe__&quot;,
+                u&quot;__sagen__&quot;,
+                u&quot;__twisted__&quot;,
</ins><span class="cx">             ),
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -553,12 +612,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;shortNames&quot;, &quot;Sanchez&quot;,
</del><ins>+                u&quot;shortNames&quot;, u&quot;Sanchez&quot;,
</ins><span class="cx">                 matchType=MatchType.contains,
</span><span class="cx">                 flags=MatchFlags.caseInsensitive,
</span><span class="cx">             ),
</span><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -566,12 +625,12 @@
</span><span class="cx">         service = self.service()
</span><span class="cx">         records = yield service.recordsFromQuery((
</span><span class="cx">             service.query(
</span><del>-                &quot;fullNames&quot;, &quot;frEdo&quot;,
</del><ins>+                u&quot;fullNames&quot;, u&quot;frEdo&quot;,
</ins><span class="cx">                 matchType=MatchType.contains,
</span><span class="cx">                 flags=MatchFlags.caseInsensitive,
</span><span class="cx">             ),
</span><span class="cx">         ))
</span><del>-        self.assertRecords(records, (&quot;__wsanchez__&quot;,))
</del><ins>+        self.assertRecords(records, (u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -580,22 +639,28 @@
</span><span class="cx">     def test_updateRecord(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
</ins><span class="cx"> 
</span><span class="cx">         fields = record.fields.copy()
</span><del>-        fields[service.fieldName.fullNames] = [&quot;Wilfredo Sanchez Vega&quot;]
</del><ins>+        fields[service.fieldName.fullNames] = [u&quot;Wilfredo Sanchez Vega&quot;]
</ins><span class="cx"> 
</span><span class="cx">         updatedRecord = DirectoryRecord(service, fields)
</span><span class="cx">         yield service.updateRecords((updatedRecord,))
</span><span class="cx"> 
</span><span class="cx">         # Verify change is present immediately
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
-        self.assertEquals(set(record.fullNames), set((&quot;Wilfredo Sanchez Vega&quot;,)))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
+        self.assertEquals(
+            set(record.fullNames),
+            set((u&quot;Wilfredo Sanchez Vega&quot;,))
+        )
</ins><span class="cx"> 
</span><span class="cx">         # Verify change is persisted
</span><span class="cx">         service.flush()
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
-        self.assertEquals(set(record.fullNames), set((&quot;Wilfredo Sanchez Vega&quot;,)))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
+        self.assertEquals(
+            set(record.fullNames),
+            set((u&quot;Wilfredo Sanchez Vega&quot;,))
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -604,23 +669,23 @@
</span><span class="cx"> 
</span><span class="cx">         newRecord = DirectoryRecord(
</span><span class="cx">             service,
</span><del>-            fields = {
-                service.fieldName.uid:        &quot;__plugh__&quot;,
</del><ins>+            fields={
+                service.fieldName.uid:        u&quot;__plugh__&quot;,
</ins><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><del>-                service.fieldName.shortNames: (&quot;plugh&quot;,),
</del><ins>+                service.fieldName.shortNames: (u&quot;plugh&quot;,),
</ins><span class="cx">             }
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         yield service.updateRecords((newRecord,), create=True)
</span><span class="cx"> 
</span><span class="cx">         # Verify change is present immediately
</span><del>-        record = (yield service.recordWithUID(&quot;__plugh__&quot;))
-        self.assertEquals(set(record.shortNames), set((&quot;plugh&quot;,)))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__plugh__&quot;))
+        self.assertEquals(set(record.shortNames), set((u&quot;plugh&quot;,)))
</ins><span class="cx"> 
</span><span class="cx">         # Verify change is persisted
</span><span class="cx">         service.flush()
</span><del>-        record = (yield service.recordWithUID(&quot;__plugh__&quot;))
-        self.assertEquals(set(record.shortNames), set((&quot;plugh&quot;,)))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__plugh__&quot;))
+        self.assertEquals(set(record.shortNames), set((u&quot;plugh&quot;,)))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_addRecordNoCreate(self):
</span><span class="lines">@@ -628,67 +693,70 @@
</span><span class="cx"> 
</span><span class="cx">         newRecord = DirectoryRecord(
</span><span class="cx">             service,
</span><del>-            fields = {
-                service.fieldName.uid:        &quot;__plugh__&quot;,
</del><ins>+            fields={
+                service.fieldName.uid:        u&quot;__plugh__&quot;,
</ins><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><del>-                service.fieldName.shortNames: (&quot;plugh&quot;,),
</del><ins>+                service.fieldName.shortNames: (u&quot;plugh&quot;,),
</ins><span class="cx">             }
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self.assertFailure(service.updateRecords((newRecord,)), NoSuchRecordError)
</del><ins>+        self.assertFailure(
+            service.updateRecords((newRecord,)),
+            NoSuchRecordError
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_removeRecord(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        yield service.removeRecords((&quot;__wsanchez__&quot;,))
</del><ins>+        yield service.removeRecords((u&quot;__wsanchez__&quot;,))
</ins><span class="cx"> 
</span><span class="cx">         # Verify change is present immediately
</span><del>-        self.assertEquals((yield service.recordWithUID(&quot;__wsanchez__&quot;)), None)
</del><ins>+        self.assertEquals((yield service.recordWithUID(u&quot;__wsanchez__&quot;)), None)
</ins><span class="cx"> 
</span><span class="cx">         # Verify change is persisted
</span><span class="cx">         service.flush()
</span><del>-        self.assertEquals((yield service.recordWithUID(&quot;__wsanchez__&quot;)), None)
</del><ins>+        self.assertEquals((yield service.recordWithUID(u&quot;__wsanchez__&quot;)), None)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_removeRecordNoExist(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        return service.removeRecords((&quot;__plugh__&quot;,))
</del><ins>+        return service.removeRecords((u&quot;__plugh__&quot;,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryRecordTest(BaseTest, test_directory.DirectoryRecordTest):
</del><ins>+class DirectoryRecordTest(BaseTest, test_directory.BaseDirectoryRecordTest):
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_members(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
</ins><span class="cx">         members = (yield record.members())
</span><span class="cx">         self.assertEquals(set(members), set())
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__twisted__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__twisted__&quot;))
</ins><span class="cx">         members = (yield record.members())
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             set((member.uid for member in members)),
</span><span class="cx">             set((
</span><del>-                &quot;__wsanchez__&quot;,
-                &quot;__glyph__&quot;,
-                &quot;__exarkun__&quot;,
-                &quot;__dreid__&quot;,
-                &quot;__dre__&quot;,
</del><ins>+                u&quot;__wsanchez__&quot;,
+                u&quot;__glyph__&quot;,
+                u&quot;__exarkun__&quot;,
+                u&quot;__dreid__&quot;,
+                u&quot;__dre__&quot;,
</ins><span class="cx">             ))
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__developers__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__developers__&quot;))
</ins><span class="cx">         members = (yield record.members())
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             set((member.uid for member in members)),
</span><span class="cx">             set((
</span><del>-                &quot;__calendar-dev__&quot;,
-                &quot;__twisted__&quot;,
-                &quot;__alyssa__&quot;,
</del><ins>+                u&quot;__calendar-dev__&quot;,
+                u&quot;__twisted__&quot;,
+                u&quot;__alyssa__&quot;,
</ins><span class="cx">             ))
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -696,13 +764,13 @@
</span><span class="cx">     def test_groups(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        record = (yield service.recordWithUID(&quot;__wsanchez__&quot;))
</del><ins>+        record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
</ins><span class="cx">         groups = (yield record.groups())
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             set(group.uid for group in groups),
</span><span class="cx">             set((
</span><del>-                &quot;__calendar-dev__&quot;,
-                &quot;__twisted__&quot;,
</del><ins>+                u&quot;__calendar-dev__&quot;,
+                u&quot;__twisted__&quot;,
</ins><span class="cx">             ))
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="lines">@@ -714,8 +782,8 @@
</span><span class="cx">         assert name is not None
</span><span class="cx">         return MatchExpression(
</span><span class="cx">             name, value,
</span><del>-            matchType = matchType,
-            flags = flags,
</del><ins>+            matchType=matchType,
+            flags=flags,
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -739,7 +807,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-testXMLConfig = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</del><ins>+testXMLConfig = b&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</ins><span class="cx"> 
</span><span class="cx"> &lt;directory realm=&quot;xyzzy&quot;&gt;
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -40,20 +40,23 @@
</span><span class="cx">         myConstants = {}
</span><span class="cx">         for constant in constants:
</span><span class="cx">             if constant.name in myConstants:
</span><del>-                raise ValueError(&quot;Name conflict: %r&quot; % (constant.name,))
</del><ins>+                raise ValueError(&quot;Name conflict: {0}&quot;.format(constant.name))
</ins><span class="cx">             myConstants[constant.name] = constant
</span><span class="cx"> 
</span><span class="cx">         self._constants = myConstants
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def __getattr__(self, name):
</span><span class="cx">         try:
</span><span class="cx">             return self._constants[name]
</span><span class="cx">         except KeyError:
</span><span class="cx">             raise AttributeError(name)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def iterconstants(self):
</span><span class="cx">         return self._constants.itervalues()
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def lookupByName(self, name):
</span><span class="cx">         try:
</span><span class="cx">             return self._constants[name]
</span><span class="lines">@@ -61,16 +64,20 @@
</span><span class="cx">             raise ValueError(name)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def uniqueResult(values):
</span><span class="cx">     result = None
</span><span class="cx">     for value in values:
</span><span class="cx">         if result is None:
</span><span class="cx">             result = value
</span><span class="cx">         else:
</span><del>-            raise DirectoryServiceError(&quot;Multiple values found where one expected.&quot;)
</del><ins>+            raise DirectoryServiceError(
+                &quot;Multiple values found where one expected.&quot;
+            )
</ins><span class="cx">     return result
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def describe(constant):
</span><span class="cx">     if isinstance(constant, FlagConstant):
</span><span class="cx">         parts = []
</span><span class="lines">@@ -81,6 +88,7 @@
</span><span class="cx">         return getattr(constant, &quot;description&quot;, constant.name)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def iterFlags(flags):
</span><span class="cx">     if hasattr(flags, &quot;__iter__&quot;):
</span><span class="cx">         return flags
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwextwhoxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twext/who/xml.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twext/who/xml.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twext/who/xml.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> ]
</span><span class="cx"> 
</span><span class="cx"> from time import time
</span><ins>+from uuid import UUID
</ins><span class="cx"> 
</span><span class="cx"> from xml.etree.ElementTree import parse as parseXML
</span><span class="cx"> from xml.etree.ElementTree import ParseError as XMLParseError
</span><span class="lines">@@ -62,38 +63,38 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> class Element(Values):
</span><del>-    directory = ValueConstant(&quot;directory&quot;)
-    record    = ValueConstant(&quot;record&quot;)
</del><ins>+    directory = ValueConstant(u&quot;directory&quot;)
+    record    = ValueConstant(u&quot;record&quot;)
</ins><span class="cx"> 
</span><span class="cx">     #
</span><span class="cx">     # Field names
</span><span class="cx">     #
</span><del>-    uid = ValueConstant(&quot;uid&quot;)
</del><ins>+    uid = ValueConstant(u&quot;uid&quot;)
</ins><span class="cx">     uid.fieldName = BaseFieldName.uid
</span><span class="cx"> 
</span><del>-    guid = ValueConstant(&quot;guid&quot;)
</del><ins>+    guid = ValueConstant(u&quot;guid&quot;)
</ins><span class="cx">     guid.fieldName = BaseFieldName.guid
</span><span class="cx"> 
</span><del>-    shortName = ValueConstant(&quot;short-name&quot;)
</del><ins>+    shortName = ValueConstant(u&quot;short-name&quot;)
</ins><span class="cx">     shortName.fieldName = BaseFieldName.shortNames
</span><span class="cx"> 
</span><del>-    fullName = ValueConstant(&quot;full-name&quot;)
</del><ins>+    fullName = ValueConstant(u&quot;full-name&quot;)
</ins><span class="cx">     fullName.fieldName = BaseFieldName.fullNames
</span><span class="cx"> 
</span><del>-    emailAddress = ValueConstant(&quot;email&quot;)
</del><ins>+    emailAddress = ValueConstant(u&quot;email&quot;)
</ins><span class="cx">     emailAddress.fieldName = BaseFieldName.emailAddresses
</span><span class="cx"> 
</span><del>-    password = ValueConstant(&quot;password&quot;)
</del><ins>+    password = ValueConstant(u&quot;password&quot;)
</ins><span class="cx">     password.fieldName = BaseFieldName.password
</span><span class="cx"> 
</span><del>-    memberUID = ValueConstant(&quot;member-uid&quot;)
</del><ins>+    memberUID = ValueConstant(u&quot;member-uid&quot;)
</ins><span class="cx">     memberUID.fieldName = IndexFieldName.memberUIDs
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class Attribute(Values):
</span><del>-    realm      = ValueConstant(&quot;realm&quot;)
-    recordType = ValueConstant(&quot;type&quot;)
</del><ins>+    realm      = ValueConstant(u&quot;realm&quot;)
+    recordType = ValueConstant(u&quot;type&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -101,16 +102,16 @@
</span><span class="cx">     #
</span><span class="cx">     # Booleans
</span><span class="cx">     #
</span><del>-    true  = ValueConstant(&quot;true&quot;)
-    false = ValueConstant(&quot;false&quot;)
</del><ins>+    true  = ValueConstant(u&quot;true&quot;)
+    false = ValueConstant(u&quot;false&quot;)
</ins><span class="cx"> 
</span><span class="cx">     #
</span><span class="cx">     # Record types
</span><span class="cx">     #
</span><del>-    user = ValueConstant(&quot;user&quot;)
</del><ins>+    user = ValueConstant(u&quot;user&quot;)
</ins><span class="cx">     user.recordType = RecordType.user
</span><span class="cx"> 
</span><del>-    group = ValueConstant(&quot;group&quot;)
</del><ins>+    group = ValueConstant(u&quot;group&quot;)
</ins><span class="cx">     group.recordType = RecordType.group
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -144,9 +145,11 @@
</span><span class="cx">         else:
</span><span class="cx">             realmName = repr(realmName)
</span><span class="cx"> 
</span><del>-        return &quot;&lt;%s %s&gt;&quot; % (
-            self.__class__.__name__,
-            realmName,
</del><ins>+        return (
+            &quot;&lt;{self.__class__.__name__} {realmName}&gt;&quot;.format(
+                self=self,
+                realmName=realmName,
+            )
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -201,7 +204,10 @@
</span><span class="cx">         #
</span><span class="cx">         if stat:
</span><span class="cx">             self.filePath.restat()
</span><del>-            cacheTag = (self.filePath.getModificationTime(), self.filePath.getsize())
</del><ins>+            cacheTag = (
+                self.filePath.getModificationTime(),
+                self.filePath.getsize()
+            )
</ins><span class="cx">             if cacheTag == self._cacheTag:
</span><span class="cx">                 return
</span><span class="cx">         else:
</span><span class="lines">@@ -225,9 +231,13 @@
</span><span class="cx">         #
</span><span class="cx">         directoryNode = etree.getroot()
</span><span class="cx">         if directoryNode.tag != self.element.directory.value:
</span><del>-            raise ParseError(&quot;Incorrect root element: %s&quot; % (directoryNode.tag,))
</del><ins>+            raise ParseError(
+                &quot;Incorrect root element: {0}&quot;.format(directoryNode.tag)
+            )
</ins><span class="cx"> 
</span><del>-        realmName = directoryNode.get(self.attribute.realm.value, &quot;&quot;).encode(&quot;utf-8&quot;)
</del><ins>+        realmName = directoryNode.get(
+            self.attribute.realm.value, u&quot;&quot;
+        )
</ins><span class="cx"> 
</span><span class="cx">         if not realmName:
</span><span class="cx">             raise ParseError(&quot;No realm name.&quot;)
</span><span class="lines">@@ -239,7 +249,9 @@
</span><span class="cx"> 
</span><span class="cx">         for recordNode in directoryNode:
</span><span class="cx">             try:
</span><del>-                records.add(self.parseRecordNode(recordNode, unknownFieldElements))
</del><ins>+                records.add(
+                    self.parseRecordNode(recordNode, unknownFieldElements)
+                )
</ins><span class="cx">             except UnknownRecordTypeError as e:
</span><span class="cx">                 unknownRecordTypes.add(e.token)
</span><span class="cx"> 
</span><span class="lines">@@ -277,10 +289,14 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def parseRecordNode(self, recordNode, unknownFieldElements=None):
</span><del>-        recordTypeAttribute = recordNode.get(self.attribute.recordType.value, &quot;&quot;).encode(&quot;utf-8&quot;)
</del><ins>+        recordTypeAttribute = recordNode.get(
+            self.attribute.recordType.value, u&quot;&quot;
+        )
</ins><span class="cx">         if recordTypeAttribute:
</span><span class="cx">             try:
</span><del>-                recordType = self.value.lookupByValue(recordTypeAttribute).recordType
</del><ins>+                recordType = (
+                    self.value.lookupByValue(recordTypeAttribute).recordType
+                )
</ins><span class="cx">             except (ValueError, AttributeError):
</span><span class="cx">                 raise UnknownRecordTypeError(recordTypeAttribute)
</span><span class="cx">         else:
</span><span class="lines">@@ -302,8 +318,16 @@
</span><span class="cx">                 if unknownFieldElements is not None:
</span><span class="cx">                     unknownFieldElements.add(fieldNode.tag)
</span><span class="cx"> 
</span><del>-            value = fieldNode.text.encode(&quot;utf-8&quot;)
</del><ins>+            vType = BaseFieldName.valueType(fieldName)
</ins><span class="cx"> 
</span><ins>+            if vType in (unicode, UUID):
+                value = unicode(fieldNode.text)
+            else:
+                raise AssertionError(
+                    &quot;Unknown value type {0} for field {1}&quot;,
+                    vType, fieldName
+                )
+
</ins><span class="cx">             if BaseFieldName.isMultiValue(fieldName):
</span><span class="cx">                 values = fields.setdefault(fieldName, [])
</span><span class="cx">                 values.append(value)
</span><span class="lines">@@ -357,9 +381,14 @@
</span><span class="cx">             for (name, value) in record.fields.items():
</span><span class="cx">                 if name == self.fieldName.recordType:
</span><span class="cx">                     if value in recordTypes:
</span><del>-                        recordNode.set(self.attribute.recordType.value, recordTypes[value])
</del><ins>+                        recordNode.set(
+                            self.attribute.recordType.value,
+                            recordTypes[value]
+                        )
</ins><span class="cx">                     else:
</span><del>-                        raise AssertionError(&quot;Unknown record type: %r&quot; % (value,))
</del><ins>+                        raise AssertionError(
+                            &quot;Unknown record type: {0}&quot;.format(value)
+                        )
</ins><span class="cx"> 
</span><span class="cx">                 else:
</span><span class="cx">                     if name in fieldNames:
</span><span class="lines">@@ -376,7 +405,9 @@
</span><span class="cx">                             recordNode.append(subNode)
</span><span class="cx"> 
</span><span class="cx">                     else:
</span><del>-                        raise AssertionError(&quot;Unknown field name: %r&quot; % (name,))
</del><ins>+                        raise AssertionError(
+                            &quot;Unknown field name: {0!r}&quot;.format(name)
+                        )
</ins><span class="cx"> 
</span><span class="cx">         # Walk through the record nodes in the XML tree and apply
</span><span class="cx">         # updates.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/config.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/config.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/config.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -97,6 +97,9 @@
</span><span class="cx">             self._defaults = ConfigDict()
</span><span class="cx">         else:
</span><span class="cx">             self._defaults = ConfigDict(copy.deepcopy(defaults))
</span><ins>+        self.importedFiles = []
+        self.includedFiles = []
+        self.missingFiles = []
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getDefaults(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryaugmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/augment.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/augment.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/augment.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -25,11 +25,11 @@
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav.config import fullServerPath, config
</span><del>-from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin,\
</del><ins>+from twistedcaldav.database import AbstractADBAPIDatabase, ADBAPISqliteMixin, \
</ins><span class="cx">     ADBAPIPostgreSQLMixin
</span><span class="cx"> from twistedcaldav.directory import xmlaugmentsparser
</span><span class="cx"> from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
</span><del>-from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement,\
</del><ins>+from twistedcaldav.xmlutil import newElementTreeWithRoot, addSubElement, \
</ins><span class="cx">     writeXML, readXML
</span><span class="cx"> from twistedcaldav.directory.util import normalizeUUID
</span><span class="cx"> 
</span><span class="lines">@@ -56,7 +56,6 @@
</span><span class="cx">         uid,
</span><span class="cx">         enabled=False,
</span><span class="cx">         serverID=&quot;&quot;,
</span><del>-        partitionID=&quot;&quot;,
</del><span class="cx">         enabledForCalendaring=False,
</span><span class="cx">         autoSchedule=False,
</span><span class="cx">         autoScheduleMode=&quot;default&quot;,
</span><span class="lines">@@ -67,7 +66,6 @@
</span><span class="cx">         self.uid = uid
</span><span class="cx">         self.enabled = enabled
</span><span class="cx">         self.serverID = serverID
</span><del>-        self.partitionID = partitionID
</del><span class="cx">         self.enabledForCalendaring = enabledForCalendaring
</span><span class="cx">         self.enabledForAddressBooks = enabledForAddressBooks
</span><span class="cx">         self.enabledForLogin = enabledForLogin
</span><span class="lines">@@ -87,9 +85,9 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Abstract base class for an augment record database.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    
</del><ins>+
</ins><span class="cx">     def __init__(self):
</span><del>-        
</del><ins>+
</ins><span class="cx">         self.cachedRecords = {}
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -126,10 +124,10 @@
</span><span class="cx"> 
</span><span class="cx">         @param uid: directory UID to lookup
</span><span class="cx">         @type uid: C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         recordType = recordTypesMap[recordType]
</span><span class="cx"> 
</span><span class="cx">         result = (yield self._lookupAugmentRecord(uid))
</span><span class="lines">@@ -166,6 +164,7 @@
</span><span class="cx">         result.clonedFromDefault = True
</span><span class="cx">         returnValue(result)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def getAllUIDs(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -173,21 +172,23 @@
</span><span class="cx"> 
</span><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         raise NotImplementedError(&quot;Child class must define this.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _lookupAugmentRecord(self, uid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Get an AugmentRecord for the specified UID.
</span><span class="cx"> 
</span><span class="cx">         @param uid: directory UID to lookup
</span><span class="cx">         @type uid: C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         raise NotImplementedError(&quot;Child class must define this.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _cachedAugmentRecord(self, uid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -195,59 +196,64 @@
</span><span class="cx"> 
</span><span class="cx">         @param uid: directory UID to lookup
</span><span class="cx">         @type uid: C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         if not uid in self.cachedRecords:
</span><span class="cx">             result = (yield self._lookupAugmentRecord(uid))
</span><span class="cx">             self.cachedRecords[uid] = result
</span><span class="cx">         returnValue(self.cachedRecords[uid])
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def addAugmentRecords(self, records):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Add an AugmentRecord to the DB.
</span><span class="cx"> 
</span><span class="cx">         @param record: augment records to add
</span><span class="cx">         @type record: C{list} of L{AugmentRecord}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         raise NotImplementedError(&quot;Child class must define this.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def removeAugmentRecords(self, uids):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove AugmentRecords with the specified UIDs.
</span><span class="cx"> 
</span><span class="cx">         @param uid: directory UIDs to remove
</span><span class="cx">         @type uid: C{list} of C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         raise NotImplementedError(&quot;Child class must define this.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def refresh(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Refresh any cached data.
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         self.cachedRecords.clear()
</span><span class="cx">         return succeed(None)
</span><del>-    
</del><ins>+
+
</ins><span class="cx">     def clean(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove all records.
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         raise NotImplementedError(&quot;Child class must define this.&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class AugmentXMLDB(AugmentDB):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     XMLFile based augment database implementation.
</span><span class="lines">@@ -257,7 +263,7 @@
</span><span class="cx"> 
</span><span class="cx">         super(AugmentXMLDB, self).__init__()
</span><span class="cx">         self.xmlFiles = [fullServerPath(config.DataRoot, path) for path in xmlFiles]
</span><del>-        self.xmlFileStats = { }
</del><ins>+        self.xmlFileStats = {}
</ins><span class="cx">         for path in self.xmlFiles:
</span><span class="cx">             self.xmlFileStats[path] = (0, 0) # mtime, size
</span><span class="cx"> 
</span><span class="lines">@@ -290,16 +296,17 @@
</span><span class="cx"> 
</span><span class="cx">         @param uid: directory UID to lookup
</span><span class="cx">         @type uid: C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         # May need to re-cache
</span><span class="cx">         if time.time() - self.lastCached &gt; self.statSeconds:
</span><span class="cx">             self.refresh()
</span><del>-            
</del><ins>+
</ins><span class="cx">         return succeed(self.db.get(uid))
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def addAugmentRecords(self, records):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Add an AugmentRecord to the DB.
</span><span class="lines">@@ -308,13 +315,13 @@
</span><span class="cx">         @type records: C{list} of L{AugmentRecord}
</span><span class="cx">         @param update: C{True} if changing an existing record
</span><span class="cx">         @type update: C{bool}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Look at each record and determine whether it is new or a modify
</span><span class="cx">         new_records = list()
</span><del>-        existing_records = list() 
</del><ins>+        existing_records = list()
</ins><span class="cx">         for record in records:
</span><span class="cx">             (existing_records if record.uid in self.db else new_records).append(record)
</span><span class="cx"> 
</span><span class="lines">@@ -332,6 +339,7 @@
</span><span class="cx"> 
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _doAddToFile(self, xmlfile, records):
</span><span class="cx"> 
</span><span class="cx">         if not os.path.exists(xmlfile):
</span><span class="lines">@@ -343,7 +351,6 @@
</span><span class="cx">             for record in self.db.itervalues():
</span><span class="cx">                 self._addRecordToXMLDB(record, augments_node)
</span><span class="cx"> 
</span><del>-
</del><span class="cx">             writeXML(xmlfile, augments_node)
</span><span class="cx"> 
</span><span class="cx">             # Set permissions
</span><span class="lines">@@ -362,33 +369,33 @@
</span><span class="cx">             if uid != -1 and gid != -1:
</span><span class="cx">                 os.chown(xmlfile, uid, gid)
</span><span class="cx"> 
</span><del>-
</del><span class="cx">         _ignore_etree, augments_node = readXML(xmlfile)
</span><span class="cx"> 
</span><span class="cx">         # Create new record
</span><span class="cx">         for record in records:
</span><span class="cx">             self._addRecordToXMLDB(record, augments_node)
</span><del>-        
</del><ins>+
</ins><span class="cx">         # Modify xmlfile
</span><span class="cx">         writeXML(xmlfile, augments_node)
</span><del>-        
</del><ins>+
+
</ins><span class="cx">     def _doModifyInFile(self, xmlfile, records):
</span><del>-    
</del><ins>+
</ins><span class="cx">         if not os.path.exists(xmlfile):
</span><span class="cx">             return
</span><span class="cx"> 
</span><span class="cx">         _ignore_etree, augments_node = readXML(xmlfile)
</span><del>-    
</del><ins>+
</ins><span class="cx">         # Map uid-&gt;record for fast lookup
</span><span class="cx">         recordMap = dict([(record.uid, record) for record in records])
</span><span class="cx"> 
</span><span class="cx">         # Make sure UID is present
</span><span class="cx">         changed = False
</span><span class="cx">         for record_node in augments_node:
</span><del>-            
</del><ins>+
</ins><span class="cx">             if record_node.tag != xmlaugmentsparser.ELEMENT_RECORD:
</span><span class="cx">                 continue
</span><del>-    
</del><ins>+
</ins><span class="cx">             uid = record_node.find(xmlaugmentsparser.ELEMENT_UID).text
</span><span class="cx">             if uid in recordMap:
</span><span class="cx">                 # Modify record
</span><span class="lines">@@ -400,13 +407,14 @@
</span><span class="cx">         if changed:
</span><span class="cx">             writeXML(xmlfile, augments_node)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def removeAugmentRecords(self, uids):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove AugmentRecords with the specified UIDs.
</span><span class="cx"> 
</span><span class="cx">         @param uids: list of uids to remove
</span><span class="cx">         @type uids: C{list} of C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -423,10 +431,11 @@
</span><span class="cx"> 
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _doRemoveFromFile(self, xmlfile, uids):
</span><del>-    
</del><ins>+
</ins><span class="cx">         _ignore_etree, augments_node = readXML(xmlfile)
</span><del>-    
</del><ins>+
</ins><span class="cx">         # Remove all UIDs present
</span><span class="cx">         changed = False
</span><span class="cx">         for child in augments_node:
</span><span class="lines">@@ -440,20 +449,19 @@
</span><span class="cx">         # Modify xmlfile
</span><span class="cx">         if changed:
</span><span class="cx">             writeXML(xmlfile, augments_node)
</span><del>-        
-        
</del><ins>+
+
</ins><span class="cx">     def _addRecordToXMLDB(self, record, parentNode):
</span><span class="cx">         record_node = addSubElement(parentNode, xmlaugmentsparser.ELEMENT_RECORD)
</span><span class="cx">         self._updateRecordInXMLDB(record, record_node)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _updateRecordInXMLDB(self, record, recordNode):
</span><span class="cx">         del recordNode[:]
</span><span class="cx">         addSubElement(recordNode, xmlaugmentsparser.ELEMENT_UID, record.uid)
</span><span class="cx">         addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLE, &quot;true&quot; if record.enabled else &quot;false&quot;)
</span><span class="cx">         if record.serverID:
</span><span class="cx">             addSubElement(recordNode, xmlaugmentsparser.ELEMENT_SERVERID, record.serverID)
</span><del>-        if record.partitionID:
-            addSubElement(recordNode, xmlaugmentsparser.ELEMENT_PARTITIONID, record.partitionID)
</del><span class="cx">         addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLECALENDAR, &quot;true&quot; if record.enabledForCalendaring else &quot;false&quot;)
</span><span class="cx">         addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLEADDRESSBOOK, &quot;true&quot; if record.enabledForAddressBooks else &quot;false&quot;)
</span><span class="cx">         addSubElement(recordNode, xmlaugmentsparser.ELEMENT_ENABLELOGIN, &quot;true&quot; if record.enabledForLogin else &quot;false&quot;)
</span><span class="lines">@@ -463,6 +471,7 @@
</span><span class="cx">         if record.autoAcceptGroup:
</span><span class="cx">             addSubElement(recordNode, xmlaugmentsparser.ELEMENT_AUTOACCEPTGROUP, record.autoAcceptGroup)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def refresh(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Refresh any cached data.
</span><span class="lines">@@ -479,6 +488,7 @@
</span><span class="cx"> 
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def clean(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove all records.
</span><span class="lines">@@ -487,6 +497,7 @@
</span><span class="cx">         self.removeAugmentRecords(self.db.keys())
</span><span class="cx">         return succeed(None)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _shouldReparse(self, xmlFiles):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Check to see whether any of the given files have been modified since
</span><span class="lines">@@ -501,6 +512,7 @@
</span><span class="cx">                     return True
</span><span class="cx">         return False
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _parseXML(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Parse self.xmlFiles into AugmentRecords.
</span><span class="lines">@@ -536,19 +548,22 @@
</span><span class="cx"> 
</span><span class="cx">         return results
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class AugmentADAPI(AugmentDB, AbstractADBAPIDatabase):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     DBAPI based augment database implementation.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    schema_version = &quot;2&quot;
-    schema_type    = &quot;AugmentDB&quot;
-    
</del><ins>+    schema_version = &quot;3&quot;
+    schema_type = &quot;AugmentDB&quot;
+
</ins><span class="cx">     def __init__(self, dbID, dbapiName, dbapiArgs, **kwargs):
</span><del>-        
</del><ins>+
</ins><span class="cx">         AugmentDB.__init__(self)
</span><span class="cx">         AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
</span><del>-        
</del><ins>+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def getAllUIDs(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -556,11 +571,12 @@
</span><span class="cx"> 
</span><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         # Query for the record information
</span><span class="cx">         results = (yield self.queryList(&quot;select UID from AUGMENTS&quot;, ()))
</span><span class="cx">         returnValue(results)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _lookupAugmentRecord(self, uid):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -571,34 +587,34 @@
</span><span class="cx"> 
</span><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        
</del><ins>+
</ins><span class="cx">         # Query for the record information
</span><del>-        results = (yield self.query(&quot;select UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1&quot;, (uid,)))
</del><ins>+        results = (yield self.query(&quot;select UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED from AUGMENTS where UID = :1&quot;, (uid,)))
</ins><span class="cx">         if not results:
</span><span class="cx">             returnValue(None)
</span><span class="cx">         else:
</span><del>-            uid, enabled, serverid, partitionid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
-            
</del><ins>+            uid, enabled, serverid, enabledForCalendaring, enabledForAddressBooks, autoSchedule, autoScheduleMode, autoAcceptGroup, enabledForLogin = results[0]
+
</ins><span class="cx">             record = AugmentRecord(
</span><del>-                uid = uid,
-                enabled = enabled == &quot;T&quot;,
-                serverID = serverid,
-                partitionID = partitionid,
-                enabledForCalendaring = enabledForCalendaring == &quot;T&quot;,
-                enabledForAddressBooks = enabledForAddressBooks == &quot;T&quot;,
-                enabledForLogin = enabledForLogin == &quot;T&quot;,
-                autoSchedule = autoSchedule == &quot;T&quot;,
-                autoScheduleMode = autoScheduleMode,
-                autoAcceptGroup = autoAcceptGroup,
</del><ins>+                uid=uid,
+                enabled=enabled == &quot;T&quot;,
+                serverID=serverid,
+                enabledForCalendaring=enabledForCalendaring == &quot;T&quot;,
+                enabledForAddressBooks=enabledForAddressBooks == &quot;T&quot;,
+                enabledForLogin=enabledForLogin == &quot;T&quot;,
+                autoSchedule=autoSchedule == &quot;T&quot;,
+                autoScheduleMode=autoScheduleMode,
+                autoAcceptGroup=autoAcceptGroup,
</ins><span class="cx">             )
</span><del>-            
</del><ins>+
</ins><span class="cx">             returnValue(record)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def addAugmentRecords(self, records):
</span><span class="cx"> 
</span><span class="cx">         for record in records:
</span><del>-            
</del><ins>+
</ins><span class="cx">             results = (yield self.query(&quot;select UID from AUGMENTS where UID = :1&quot;, (record.uid,)))
</span><span class="cx">             update = len(results) &gt; 0
</span><span class="cx"> 
</span><span class="lines">@@ -607,6 +623,7 @@
</span><span class="cx">             else:
</span><span class="cx">                 yield self._addRecord(record)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def removeAugmentRecords(self, uids):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -614,32 +631,36 @@
</span><span class="cx"> 
</span><span class="cx">         @param uids: list of uids to remove
</span><span class="cx">         @type uids: C{list} of C{str}
</span><del>-        
</del><ins>+
</ins><span class="cx">         @return: L{Deferred}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         for uid in uids:
</span><span class="cx">             yield self.execute(&quot;delete from AUGMENTS where UID = :1&quot;, (uid,))
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def clean(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Remove all records.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         return self.execute(&quot;delete from AUGMENTS&quot;, ())
</span><del>-        
</del><ins>+
+
</ins><span class="cx">     def _db_version(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: the schema version assigned to this index.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return AugmentADAPI.schema_version
</span><del>-        
</del><ins>+
+
</ins><span class="cx">     def _db_type(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: the collection type assigned to this index.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return AugmentADAPI.schema_type
</span><del>-    
</del><ins>+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _db_init_data_tables(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -652,45 +673,47 @@
</span><span class="cx">         yield self._create_table(
</span><span class="cx">             &quot;AUGMENTS&quot;,
</span><span class="cx">             (
</span><del>-                (&quot;UID&quot;,              &quot;text unique&quot;),
-                (&quot;ENABLED&quot;,          &quot;text(1)&quot;),
-                (&quot;SERVERID&quot;,         &quot;text&quot;),
-                (&quot;PARTITIONID&quot;,      &quot;text&quot;),
-                (&quot;CALENDARING&quot;,      &quot;text(1)&quot;),
-                (&quot;ADDRESSBOOKS&quot;,     &quot;text(1)&quot;),
-                (&quot;AUTOSCHEDULE&quot;,     &quot;text(1)&quot;),
</del><ins>+                (&quot;UID&quot;, &quot;text unique&quot;),
+                (&quot;ENABLED&quot;, &quot;text(1)&quot;),
+                (&quot;SERVERID&quot;, &quot;text&quot;),
+                (&quot;CALENDARING&quot;, &quot;text(1)&quot;),
+                (&quot;ADDRESSBOOKS&quot;, &quot;text(1)&quot;),
+                (&quot;AUTOSCHEDULE&quot;, &quot;text(1)&quot;),
</ins><span class="cx">                 (&quot;AUTOSCHEDULEMODE&quot;, &quot;text&quot;),
</span><del>-                (&quot;AUTOACCEPTGROUP&quot;,  &quot;text&quot;),
-                (&quot;LOGINENABLED&quot;,     &quot;text(1)&quot;),
</del><ins>+                (&quot;AUTOACCEPTGROUP&quot;, &quot;text&quot;),
+                (&quot;LOGINENABLED&quot;, &quot;text(1)&quot;),
</ins><span class="cx">             ),
</span><span class="cx">             ifnotexists=True,
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _db_empty_data_tables(self):
</span><span class="cx">         yield self._db_execute(&quot;delete from AUGMENTS&quot;)
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class AugmentSqliteDB(ADBAPISqliteMixin, AugmentADAPI):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Sqlite based augment database implementation.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, dbpath):
</span><del>-        
</del><ins>+
</ins><span class="cx">         ADBAPISqliteMixin.__init__(self)
</span><span class="cx">         AugmentADAPI.__init__(self, &quot;Augments&quot;, &quot;sqlite3&quot;, (fullServerPath(config.DataRoot, dbpath),))
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _addRecord(self, record):
</span><span class="cx">         yield self.execute(
</span><span class="cx">             &quot;&quot;&quot;insert or replace into AUGMENTS
</span><del>-            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
-            values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)&quot;&quot;&quot;,
</del><ins>+            (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+            values (:1, :2, :3, :4, :5, :6, :7, :8, :9)&quot;&quot;&quot;,
</ins><span class="cx">             (
</span><span class="cx">                 record.uid,
</span><span class="cx">                 &quot;T&quot; if record.enabled else &quot;F&quot;,
</span><span class="cx">                 record.serverID,
</span><del>-                record.partitionID,
</del><span class="cx">                 &quot;T&quot; if record.enabledForCalendaring else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.enabledForAddressBooks else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.autoSchedule else &quot;F&quot;,
</span><span class="lines">@@ -700,30 +723,33 @@
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _modifyRecord(self, record):
</span><span class="cx">         return self._addRecord(record)
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class AugmentPostgreSQLDB(ADBAPIPostgreSQLMixin, AugmentADAPI):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     PostgreSQL based augment database implementation.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, host, database, user=None, password=None):
</span><del>-        
</del><ins>+
</ins><span class="cx">         ADBAPIPostgreSQLMixin.__init__(self)
</span><span class="cx">         AugmentADAPI.__init__(self, &quot;Augments&quot;, &quot;pgdb&quot;, (), host=host, database=database, user=user, password=password,)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _addRecord(self, record):
</span><span class="cx">         yield self.execute(
</span><span class="cx">             &quot;&quot;&quot;insert into AUGMENTS
</span><del>-            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
-            values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)&quot;&quot;&quot;,
</del><ins>+            (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED)
+            values (:1, :2, :3, :4, :5, :6, :7, :8, :9)&quot;&quot;&quot;,
</ins><span class="cx">             (
</span><span class="cx">                 record.uid,
</span><span class="cx">                 &quot;T&quot; if record.enabled else &quot;F&quot;,
</span><span class="cx">                 record.serverID,
</span><del>-                record.partitionID,
</del><span class="cx">                 &quot;T&quot; if record.enabledForCalendaring else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.enabledForAddressBooks else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.autoSchedule else &quot;F&quot;,
</span><span class="lines">@@ -733,17 +759,17 @@
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def _modifyRecord(self, record):
</span><span class="cx">         yield self.execute(
</span><span class="cx">             &quot;&quot;&quot;update AUGMENTS set
</span><del>-            (UID, ENABLED, SERVERID, PARTITIONID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
-            (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10) where UID = :11&quot;&quot;&quot;,
</del><ins>+            (UID, ENABLED, SERVERID, CALENDARING, ADDRESSBOOKS, AUTOSCHEDULE, AUTOSCHEDULEMODE, AUTOACCEPTGROUP, LOGINENABLED) =
+            (:1, :2, :3, :4, :5, :6, :7, :8, :9) where UID = :10&quot;&quot;&quot;,
</ins><span class="cx">             (
</span><span class="cx">                 record.uid,
</span><span class="cx">                 &quot;T&quot; if record.enabled else &quot;F&quot;,
</span><span class="cx">                 record.serverID,
</span><del>-                record.partitionID,
</del><span class="cx">                 &quot;T&quot; if record.enabledForCalendaring else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.enabledForAddressBooks else &quot;F&quot;,
</span><span class="cx">                 &quot;T&quot; if record.autoSchedule else &quot;F&quot;,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorycommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/common.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/common.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/common.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx">     Common ancestor for addressbook/calendar UID provisioning resources.
</span><span class="cx"> 
</span><span class="cx">     Must be mixed in to the hierarchy I{before} the appropriate resource type.
</span><del>-    
</del><ins>+
</ins><span class="cx">     @ivar homeResourceTypeName: The name of the home resource type ('calendars'
</span><span class="cx">         or 'addressbooks').
</span><span class="cx"> 
</span><span class="lines">@@ -78,13 +78,11 @@
</span><span class="cx"> 
</span><span class="cx">         assert len(name) &gt; 4, &quot;Directory record has an invalid GUID: %r&quot; % (
</span><span class="cx">             name,)
</span><del>-        
-        if record.locallyHosted():
</del><ins>+
+        if record.thisServer():
</ins><span class="cx">             child = yield self.homeResourceCreator(record, transaction)
</span><del>-        elif record.thisServer():
</del><ins>+        else:
</ins><span class="cx">             child = DirectoryReverseProxyResource(self, record)
</span><del>-        else:
-            child = None # Use a redirect?
</del><span class="cx"> 
</span><span class="cx">         returnValue(child)
</span><span class="cx"> 
</span><span class="lines">@@ -108,6 +106,7 @@
</span><span class="cx">         # Not a listable collection
</span><span class="cx">         raise HTTPError(responsecode.FORBIDDEN)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     ##
</span><span class="cx">     # ACL
</span><span class="cx">     ##
</span><span class="lines">@@ -115,12 +114,15 @@
</span><span class="cx">     def principalCollections(self):
</span><span class="cx">         return self.parent.principalCollections()
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def principalForRecord(self, record):
</span><span class="cx">         return self.parent.principalForRecord(record)
</span><ins>+
+
</ins><span class="cx">     ##
</span><span class="cx">     # DAV
</span><span class="cx">     ##
</span><del>-    
</del><ins>+
</ins><span class="cx">     def isCollection(self):
</span><span class="cx">         return True
</span><span class="cx"> 
</span><span class="lines">@@ -129,9 +131,11 @@
</span><span class="cx">         raise NotImplementedError(self.__class__.__name__ +
</span><span class="cx">                                   &quot;.getChild no longer exists.&quot;)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def displayName(self):
</span><span class="cx">         return uidsResourceName
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def url(self):
</span><span class="cx">         return joinURL(self.parent.url(), uidsResourceName)
</span><span class="cx"> 
</span><span class="lines">@@ -153,4 +157,3 @@
</span><span class="cx"> 
</span><span class="cx">         child = yield self._parent.homeForDirectoryRecord(record, request)
</span><span class="cx">         returnValue((child, segments[1:]))
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorydirectoryprincipalresourcehtml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory-principal-resource.html (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory-principal-resource.html        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory-principal-resource.html        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -5,8 +5,7 @@
</span><span class="cx"> ---------------------
</span><span class="cx"> Directory GUID: &lt;t:slot name=&quot;directoryGUID&quot;/&gt;
</span><span class="cx"> Realm: &lt;t:slot name=&quot;realm&quot;/&gt;
</span><del>-&lt;t:transparent t:render=&quot;serversEnabled&quot;&gt;Hosted-At: &lt;t:slot name=&quot;hostedAt&quot;/&gt;
-Partition: &lt;t:slot name=&quot;partition&quot;/&gt;&lt;/t:transparent&gt;
</del><ins>+&lt;t:transparent t:render=&quot;serversEnabled&quot;&gt;Hosted-At: &lt;t:slot name=&quot;hostedAt&quot;/&gt;&lt;/t:transparent&gt;
</ins><span class="cx"> Principal Information
</span><span class="cx"> ---------------------
</span><span class="cx"> GUID: &lt;t:slot name=&quot;principalGUID&quot;/&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/directory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -89,8 +89,8 @@
</span><span class="cx"> 
</span><span class="cx">     searchContext_location = &quot;location&quot;
</span><span class="cx">     searchContext_resource = &quot;resource&quot;
</span><del>-    searchContext_user     = &quot;user&quot;
-    searchContext_group    = &quot;group&quot;
</del><ins>+    searchContext_user = &quot;user&quot;
+    searchContext_group = &quot;group&quot;
</ins><span class="cx">     searchContext_attendee = &quot;attendee&quot;
</span><span class="cx"> 
</span><span class="cx">     aggregateService = None
</span><span class="lines">@@ -533,10 +533,11 @@
</span><span class="cx">         )
</span><span class="cx">         for record in resources:
</span><span class="cx">             guid = record.guid
</span><del>-            assignments.append((&quot;%s#calendar-proxy-write&quot; % (guid,),
-                               record.externalProxies()))
-            assignments.append((&quot;%s#calendar-proxy-read&quot; % (guid,),
-                               record.externalReadOnlyProxies()))
</del><ins>+            if record.enabledForCalendaring:
+                assignments.append((&quot;%s#calendar-proxy-write&quot; % (guid,),
+                                   record.externalProxies()))
+                assignments.append((&quot;%s#calendar-proxy-read&quot; % (guid,),
+                                   record.externalReadOnlyProxies()))
</ins><span class="cx"> 
</span><span class="cx">         return assignments
</span><span class="cx"> 
</span><span class="lines">@@ -627,6 +628,7 @@
</span><span class="cx">         self.expireSeconds = expireSeconds
</span><span class="cx">         self.lockSeconds = lockSeconds
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def setGroupsFor(self, guid, memberships):
</span><span class="cx">         self.log.debug(&quot;set groups-for %s : %s&quot; % (guid, memberships))
</span><span class="cx">         return self.set(&quot;groups-for:%s&quot; %
</span><span class="lines">@@ -674,7 +676,6 @@
</span><span class="cx">         return self.add(&quot;group-cacher-lock&quot;, &quot;1&quot;, expireTime=self.lockSeconds)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx">     def extendLock(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Update the expiration time of the memcached lock
</span><span class="lines">@@ -693,6 +694,7 @@
</span><span class="cx">         return self.delete(&quot;group-cacher-lock&quot;)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class GroupMembershipCacheUpdater(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Responsible for updating memcached with group memberships.  This will run
</span><span class="lines">@@ -1129,7 +1131,7 @@
</span><span class="cx">     implements(IDirectoryRecord, ICalendarStoreDirectoryRecord)
</span><span class="cx"> 
</span><span class="cx">     def __repr__(self):
</span><del>-        return &quot;&lt;%s[%s@%s(%s)] %s(%s) %r @ %s/#%s&gt;&quot; % (
</del><ins>+        return &quot;&lt;%s[%s@%s(%s)] %s(%s) %r @ %s&gt;&quot; % (
</ins><span class="cx">             self.__class__.__name__,
</span><span class="cx">             self.recordType,
</span><span class="cx">             self.service.guid,
</span><span class="lines">@@ -1138,7 +1140,6 @@
</span><span class="cx">             &quot;,&quot;.join(self.shortNames),
</span><span class="cx">             self.fullName,
</span><span class="cx">             self.serverURI(),
</span><del>-            self.partitionID,
</del><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1174,7 +1175,6 @@
</span><span class="cx">         self.uid = uid
</span><span class="cx">         self.enabled = False
</span><span class="cx">         self.serverID = &quot;&quot;
</span><del>-        self.partitionID = &quot;&quot;
</del><span class="cx">         self.shortNames = shortNames
</span><span class="cx">         self.authIDs = authIDs
</span><span class="cx">         self.fullName = fullName
</span><span class="lines">@@ -1256,7 +1256,6 @@
</span><span class="cx">         if augment:
</span><span class="cx">             self.enabled = augment.enabled
</span><span class="cx">             self.serverID = augment.serverID
</span><del>-            self.partitionID = augment.partitionID
</del><span class="cx">             self.enabledForCalendaring = augment.enabledForCalendaring
</span><span class="cx">             self.enabledForAddressBooks = augment.enabledForAddressBooks
</span><span class="cx">             self.autoSchedule = augment.autoSchedule
</span><span class="lines">@@ -1277,7 +1276,6 @@
</span><span class="cx">             # Groups are by default always enabled
</span><span class="cx">             self.enabled = (self.recordType == self.service.recordType_groups)
</span><span class="cx">             self.serverID = &quot;&quot;
</span><del>-            self.partitionID = &quot;&quot;
</del><span class="cx">             self.enabledForCalendaring = False
</span><span class="cx">             self.enabledForAddressBooks = False
</span><span class="cx">             self.enabledForLogin = False
</span><span class="lines">@@ -1495,46 +1493,9 @@
</span><span class="cx">             return None
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def partitionURI(self):
-        &quot;&quot;&quot;
-        URL of the server hosting this record. Return None if hosted on this server.
-        &quot;&quot;&quot;
-        if config.Servers.Enabled and self.serverID:
-            s = Servers.getServerById(self.serverID)
-            if s:
-                return s.getPartitionURIForId(self.partitionID)
-        return None
-
-
-    def locallyHosted(self):
-        &quot;&quot;&quot;
-        Hosted on this server/partition instance.
-        &quot;&quot;&quot;
-
-        if config.Servers.Enabled and self.serverID:
-            s = Servers.getServerById(self.serverID)
-            if s:
-                return s.thisServer and (not s.isPartitioned() or not self.partitionID or self.partitionID == config.ServerPartitionID)
-        return True
-
-
-    def effectivePartitionID(self):
-        &quot;&quot;&quot;
-        Record partition ID taking into account whether the server is partitioned.
-        &quot;&quot;&quot;
-        if config.Servers.Enabled and self.serverID:
-            s = Servers.getServerById(self.serverID)
-            if s and s.isPartitioned():
-                return self.partitionID
-        return &quot;&quot;
-
-
</del><span class="cx">     def thisServer(self):
</span><del>-        if config.Servers.Enabled and self.serverID:
-            s = Servers.getServerById(self.serverID)
-            if s:
-                return s.thisServer
-        return True
</del><ins>+        s = self.server()
+        return s.thisServer if s is not None else True
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def autoAcceptMembers(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryidirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/idirectory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/idirectory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/idirectory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -32,20 +32,20 @@
</span><span class="cx">     realmName = Attribute(&quot;The name of the authentication realm this service represents.&quot;)
</span><span class="cx">     guid = Attribute(&quot;A GUID for this service.&quot;)
</span><span class="cx"> 
</span><del>-    def recordTypes():
</del><ins>+    def recordTypes(): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: a sequence of strings denoting the record types that
</span><span class="cx">             are kept in the directory.  For example: C{[&quot;users&quot;,
</span><span class="cx">             &quot;groups&quot;, &quot;resources&quot;]}.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def listRecords(recordType):
</del><ins>+    def listRecords(recordType): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param type: the type of records to retrieve.
</span><span class="cx">         @return: an iterable of records of the given type.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordWithShortName(recordType, shortName):
</del><ins>+    def recordWithShortName(recordType, shortName): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param recordType: the type of the record to look up.
</span><span class="cx">         @param shortName: the short name of the record to look up.
</span><span class="lines">@@ -53,21 +53,21 @@
</span><span class="cx">             C{None} if no such record exists.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordWithUID(uid):
</del><ins>+    def recordWithUID(uid): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param uid: the UID of the record to look up.
</span><span class="cx">         @return: an L{IDirectoryRecord} with the given UID, or C{None}
</span><span class="cx">             if no such record exists.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordWithGUID(guid):
</del><ins>+    def recordWithGUID(guid): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param guid: the GUID of the record to look up.
</span><span class="cx">         @return: an L{IDirectoryRecord} with the given GUID, or
</span><span class="cx">             C{None} if no such record exists.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordWithCalendarUserAddress(address):
</del><ins>+    def recordWithCalendarUserAddress(address): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param address: the calendar user address of the record to look up.
</span><span class="cx">         @type address: C{str}
</span><span class="lines">@@ -81,7 +81,7 @@
</span><span class="cx">             directory service may not be aware of these addresses.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordWithCachedGroupsAlias(recordType, alias):
</del><ins>+    def recordWithCachedGroupsAlias(recordType, alias): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param recordType: the type of the record to look up.
</span><span class="cx">         @param alias: the cached-groups alias of the record to look up.
</span><span class="lines">@@ -91,14 +91,13 @@
</span><span class="cx">             alias, or C{None} if no such record is found.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-
-    def recordsMatchingFields(fields):
</del><ins>+    def recordsMatchingFields(fields): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: a deferred sequence of L{IDirectoryRecord}s which
</span><span class="cx">             match the given fields.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def recordsMatchingTokens(tokens, context=None):
</del><ins>+    def recordsMatchingTokens(tokens, context=None): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param tokens: The tokens to search on
</span><span class="cx">         @type tokens: C{list} of C{str} (utf-8 bytes)
</span><span class="lines">@@ -119,31 +118,31 @@
</span><span class="cx">             &quot;attendee&quot;, only users, groups, and resources are considered.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-
-    def setRealm(realmName):
</del><ins>+    def setRealm(realmName): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Set a new realm name for this (and nested services if any)
</span><span class="cx"> 
</span><span class="cx">         @param realmName: the realm name this service should use.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class IDirectoryRecord(Interface):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Directory Record
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    service               = Attribute(&quot;The L{IDirectoryService} this record exists in.&quot;)
-    recordType            = Attribute(&quot;The type of this record.&quot;)
-    guid                  = Attribute(&quot;The GUID of this record.&quot;)
-    uid                   = Attribute(&quot;The UID of this record.&quot;)
-    enabled               = Attribute(&quot;Determines whether this record should allow a principal to be created.&quot;)
-    serverID              = Attribute(&quot;Identifies the server that actually hosts data for the record.&quot;)
-    partitionID           = Attribute(&quot;Identifies the partition node that actually hosts data for the record.&quot;)
-    shortNames            = Attribute(&quot;The names for this record.&quot;)
-    authIDs               = Attribute(&quot;Alternative security identities for this record.&quot;)
-    fullName              = Attribute(&quot;The full name of this record.&quot;)
-    firstName             = Attribute(&quot;The first name of this record.&quot;)
-    lastName              = Attribute(&quot;The last name of this record.&quot;)
-    emailAddresses        = Attribute(&quot;The email addresses of this record.&quot;)
</del><ins>+    service = Attribute(&quot;The L{IDirectoryService} this record exists in.&quot;)
+    recordType = Attribute(&quot;The type of this record.&quot;)
+    guid = Attribute(&quot;The GUID of this record.&quot;)
+    uid = Attribute(&quot;The UID of this record.&quot;)
+    enabled = Attribute(&quot;Determines whether this record should allow a principal to be created.&quot;)
+    serverID = Attribute(&quot;Identifies the server that actually hosts data for the record.&quot;)
+    shortNames = Attribute(&quot;The names for this record.&quot;)
+    authIDs = Attribute(&quot;Alternative security identities for this record.&quot;)
+    fullName = Attribute(&quot;The full name of this record.&quot;)
+    firstName = Attribute(&quot;The first name of this record.&quot;)
+    lastName = Attribute(&quot;The last name of this record.&quot;)
+    emailAddresses = Attribute(&quot;The email addresses of this record.&quot;)
</ins><span class="cx">     enabledForCalendaring = Attribute(&quot;Determines whether this record creates a principal with a calendar home.&quot;)
</span><span class="cx">     enabledForAddressBooks = Attribute(&quot;Determines whether this record creates a principal with an address book home.&quot;)
</span><span class="cx">     calendarUserAddresses = Attribute(
</span><span class="lines">@@ -159,19 +158,19 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">     )
</span><span class="cx"> 
</span><del>-    def members():
</del><ins>+    def members(): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: an iterable of L{IDirectoryRecord}s for the members of this
</span><span class="cx">             (group) record.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def groups():
</del><ins>+    def groups(): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @return: an iterable of L{IDirectoryRecord}s for the groups this
</span><span class="cx">             record is a member of.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def verifyCredentials(credentials):
</del><ins>+    def verifyCredentials(credentials): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Verify that the given credentials can authenticate the principal
</span><span class="cx">         represented by this record.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/ldapdirectory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/ldapdirectory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/ldapdirectory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -75,12 +75,13 @@
</span><span class="cx">         return &quot;&lt;%s %r: %r&gt;&quot; % (self.__class__.__name__, self.realmName,
</span><span class="cx">             self.uri)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def __init__(self, params):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param params: a dictionary containing the following keys:
</span><span class="cx">             cacheTimeout, realmName, uri, tls, tlsCACertFile, tlsCACertDir,
</span><span class="cx">             tlsRequireCert, credentials, rdnSchema, groupSchema, resourceSchema
</span><del>-            partitionSchema
</del><ins>+            poddingSchema
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         defaults = {
</span><span class="lines">@@ -188,9 +189,8 @@
</span><span class="cx">                 &quot;readOnlyProxyAttr&quot;: None, # list of GUIDs
</span><span class="cx">                 &quot;autoAcceptGroupAttr&quot;: None, # single group GUID
</span><span class="cx">             },
</span><del>-            &quot;partitionSchema&quot;: {
</del><ins>+            &quot;poddingSchema&quot;: {
</ins><span class="cx">                 &quot;serverIdAttr&quot;: None, # maps to augments server-id
</span><del>-                &quot;partitionIdAttr&quot;: None, # maps to augments partition-id
</del><span class="cx">             },
</span><span class="cx">         }
</span><span class="cx">         ignored = None
</span><span class="lines">@@ -222,7 +222,7 @@
</span><span class="cx">         self.rdnSchema = params[&quot;rdnSchema&quot;]
</span><span class="cx">         self.groupSchema = params[&quot;groupSchema&quot;]
</span><span class="cx">         self.resourceSchema = params[&quot;resourceSchema&quot;]
</span><del>-        self.partitionSchema = params[&quot;partitionSchema&quot;]
</del><ins>+        self.poddingSchema = params[&quot;poddingSchema&quot;]
</ins><span class="cx"> 
</span><span class="cx">         self.base = ldap.dn.str2dn(self.rdnSchema[&quot;base&quot;])
</span><span class="cx"> 
</span><span class="lines">@@ -272,10 +272,8 @@
</span><span class="cx">             attrSet.add(self.resourceSchema[&quot;proxyAttr&quot;])
</span><span class="cx">         if self.resourceSchema[&quot;readOnlyProxyAttr&quot;]:
</span><span class="cx">             attrSet.add(self.resourceSchema[&quot;readOnlyProxyAttr&quot;])
</span><del>-        if self.partitionSchema[&quot;serverIdAttr&quot;]:
-            attrSet.add(self.partitionSchema[&quot;serverIdAttr&quot;])
-        if self.partitionSchema[&quot;partitionIdAttr&quot;]:
-            attrSet.add(self.partitionSchema[&quot;partitionIdAttr&quot;])
</del><ins>+        if self.poddingSchema[&quot;serverIdAttr&quot;]:
+            attrSet.add(self.poddingSchema[&quot;serverIdAttr&quot;])
</ins><span class="cx">         self.attrlist = list(attrSet)
</span><span class="cx"> 
</span><span class="cx">         self.typeDNs = {}
</span><span class="lines">@@ -284,10 +282,8 @@
</span><span class="cx">                 self.rdnSchema[recordType][&quot;rdn&quot;].lower()
</span><span class="cx">             ) + self.base
</span><span class="cx"> 
</span><del>-
</del><span class="cx">         self.ldap = None
</span><span class="cx"> 
</span><del>-
</del><span class="cx">         # Separate LDAP connection used solely for authenticating clients
</span><span class="cx">         self.authLDAP = None
</span><span class="cx"> 
</span><span class="lines">@@ -314,7 +310,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Query the LDAP server
</span><span class="cx">         self.log.debug(&quot;Querying ldap for records matching base {base} and &quot;
</span><del>-            &quot;filter {filter} for attributes {attrs}.&quot;, 
</del><ins>+            &quot;filter {filter} for attributes {attrs}.&quot;,
</ins><span class="cx">             base=ldap.dn.dn2str(base), filter=filterstr, attrs=self.attrlist)
</span><span class="cx"> 
</span><span class="cx">         # This takes a while, so if you don't want to have a &quot;long request&quot;
</span><span class="lines">@@ -353,6 +349,7 @@
</span><span class="cx"> 
</span><span class="cx">         return records
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def recordWithCachedGroupsAlias(self, recordType, alias):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -373,6 +370,7 @@
</span><span class="cx">         else:
</span><span class="cx">             returnValue(None)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def getExternalProxyAssignments(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Retrieve proxy assignments for locations and resources from the
</span><span class="lines">@@ -391,6 +389,12 @@
</span><span class="cx"> 
</span><span class="cx">         # Build filter
</span><span class="cx">         filterstr = &quot;(|(%s=*)(%s=*))&quot; % (readAttr, writeAttr)
</span><ins>+        # ...taking into account only calendar-enabled records
+        enabledAttr = self.rdnSchema[&quot;locations&quot;][&quot;calendarEnabledAttr&quot;]
+        enabledValue = self.rdnSchema[&quot;locations&quot;][&quot;calendarEnabledValue&quot;]
+        if enabledAttr and enabledValue:
+            filterstr = &quot;(&amp;(%s=%s)%s)&quot; % (enabledAttr, enabledValue, filterstr)
+
</ins><span class="cx">         attrlist = [guidAttr, readAttr, writeAttr]
</span><span class="cx"> 
</span><span class="cx">         # Query the LDAP server
</span><span class="lines">@@ -419,6 +423,7 @@
</span><span class="cx"> 
</span><span class="cx">         return assignments
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def getLDAPConnection(self):
</span><span class="cx">         if self.ldap is None:
</span><span class="cx">             self.log.info(&quot;Connecting to LDAP {uri}&quot;, uri=repr(self.uri))
</span><span class="lines">@@ -437,6 +442,7 @@
</span><span class="cx">                     raise DirectoryConfigurationError()
</span><span class="cx">         return self.ldap
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def createLDAPConnection(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Create and configure LDAP connection
</span><span class="lines">@@ -472,7 +478,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         TRIES = 3
</span><span class="cx"> 
</span><del>-        for i in xrange(TRIES):
</del><ins>+        for _ignore_i in xrange(TRIES):
</ins><span class="cx">             self.log.debug(&quot;Authenticating %s&quot; % (dn,))
</span><span class="cx"> 
</span><span class="cx">             if self.authLDAP is None:
</span><span class="lines">@@ -549,11 +555,11 @@
</span><span class="cx">                 self.log.warn(&quot;LDAP timeout exceeded: %d seconds&quot; % (timeoutSeconds,))
</span><span class="cx">             except ldap.SERVER_DOWN:
</span><span class="cx">                 self.ldap = None
</span><del>-                self.log.error(&quot;LDAP server unavailable (tried %d times)&quot; % (i+1,))
</del><ins>+                self.log.error(&quot;LDAP server unavailable (tried %d times)&quot; % (i + 1,))
</ins><span class="cx">                 continue
</span><span class="cx"> 
</span><span class="cx">             # change format, ignoring resultsType
</span><del>-            result = [resultItem for resultType, resultItem in s.allResults]
</del><ins>+            result = [resultItem for _ignore_resultType, resultItem in s.allResults]
</ins><span class="cx"> 
</span><span class="cx">             totalTime = time.time() - startTime
</span><span class="cx">             if totalTime &gt; self.warningThresholdSeconds:
</span><span class="lines">@@ -763,7 +769,6 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         guid = None
</span><del>-        shortNames = ()
</del><span class="cx">         authIDs = set()
</span><span class="cx">         fullName = None
</span><span class="cx">         firstName = &quot;&quot;
</span><span class="lines">@@ -885,30 +890,27 @@
</span><span class="cx">                     autoAcceptGroup = self._getUniqueLdapAttribute(attrs,
</span><span class="cx">                         self.resourceSchema[&quot;autoAcceptGroupAttr&quot;])
</span><span class="cx"> 
</span><del>-        serverID = partitionID = None
-        if self.partitionSchema[&quot;serverIdAttr&quot;]:
</del><ins>+        serverID = None
+        if self.poddingSchema[&quot;serverIdAttr&quot;]:
</ins><span class="cx">             serverID = self._getUniqueLdapAttribute(attrs,
</span><del>-                self.partitionSchema[&quot;serverIdAttr&quot;])
-        if self.partitionSchema[&quot;partitionIdAttr&quot;]:
-            partitionID = self._getUniqueLdapAttribute(attrs,
-                self.partitionSchema[&quot;partitionIdAttr&quot;])
</del><ins>+                self.poddingSchema[&quot;serverIdAttr&quot;])
</ins><span class="cx"> 
</span><span class="cx">         record = LdapDirectoryRecord(
</span><del>-            service                 = self,
-            recordType              = recordType,
-            guid                    = guid,
-            shortNames              = shortNames,
-            authIDs                 = authIDs,
-            fullName                = fullName,
-            firstName               = firstName,
-            lastName                = lastName,
-            emailAddresses          = emailAddresses,
-            uid                     = uid,
-            dn                      = dn,
-            memberGUIDs             = memberGUIDs,
-            extProxies              = proxyGUIDs,
-            extReadOnlyProxies      = readOnlyProxyGUIDs,
-            attrs                   = attrs,
</del><ins>+            service=self,
+            recordType=recordType,
+            guid=guid,
+            shortNames=shortNames,
+            authIDs=authIDs,
+            fullName=fullName,
+            firstName=firstName,
+            lastName=lastName,
+            emailAddresses=emailAddresses,
+            uid=uid,
+            dn=dn,
+            memberGUIDs=memberGUIDs,
+            extProxies=proxyGUIDs,
+            extReadOnlyProxies=readOnlyProxyGUIDs,
+            attrs=attrs,
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         if self.augmentService is not None:
</span><span class="lines">@@ -918,7 +920,7 @@
</span><span class="cx">             # immediately.
</span><span class="cx">             d = self.augmentService.getAugmentRecord(record.guid,
</span><span class="cx">                 recordType)
</span><del>-            d.addCallback(lambda x:record.addAugmentInformation(x))
</del><ins>+            d.addCallback(lambda x: record.addAugmentInformation(x))
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             # Generate augment record based on information retrieved from LDAP
</span><span class="lines">@@ -926,7 +928,6 @@
</span><span class="cx">                 guid,
</span><span class="cx">                 enabled=True,
</span><span class="cx">                 serverID=serverID,
</span><del>-                partitionID=partitionID,
</del><span class="cx">                 enabledForCalendaring=enabledForCalendaring,
</span><span class="cx">                 autoSchedule=autoSchedule,
</span><span class="cx">                 autoAcceptGroup=autoAcceptGroup,
</span><span class="lines">@@ -959,7 +960,7 @@
</span><span class="cx">         matching the indexType and indexKey parameters.
</span><span class="cx"> 
</span><span class="cx">         recordTypes is a list of record types to limit the search to.
</span><del>-        indexType specifies one of the CachingDirectoryService contstants
</del><ins>+        indexType specifies one of the CachingDirectoryService constants
</ins><span class="cx">             identifying which attribute to search on.
</span><span class="cx">         indexKey is the value to search for.
</span><span class="cx"> 
</span><span class="lines">@@ -988,7 +989,8 @@
</span><span class="cx">                 # Query on guid only works if guid attribute has been defined.
</span><span class="cx">                 # Support for query on guid even if is auto-generated should
</span><span class="cx">                 # be added.
</span><del>-                if not guidAttr: return
</del><ins>+                if not guidAttr:
+                    return
</ins><span class="cx">                 filterstr = &quot;(&amp;%s(%s=%s))&quot; % (filterstr, guidAttr, indexKey)
</span><span class="cx"> 
</span><span class="cx">             elif indexType == self.INDEX_TYPE_SHORTNAME:
</span><span class="lines">@@ -1046,7 +1048,7 @@
</span><span class="cx"> 
</span><span class="cx">                 try:
</span><span class="cx">                     record = self._ldapResultToRecord(dn, attrs, recordType)
</span><del>-                    self.log.debug(&quot;Got LDAP record %s&quot; % (record,))
</del><ins>+                    self.log.debug(&quot;Got LDAP record {rec}&quot;, rec=record)
</ins><span class="cx"> 
</span><span class="cx">                     if not unrestricted:
</span><span class="cx">                         self.log.debug(&quot;%s is not enabled because it's not a member of group: %s&quot; % (dn, self.restrictToGroup))
</span><span class="lines">@@ -1151,7 +1153,6 @@
</span><span class="cx"> 
</span><span class="cx">                 self.log.debug(&quot;LDAP search returned %d results, %d usable&quot; % (len(results), typeCounts[recordType]))
</span><span class="cx"> 
</span><del>-
</del><span class="cx">         typeCountsStr = &quot;, &quot;.join([&quot;%s:%d&quot; % (rt, ct) for (rt, ct) in typeCounts.iteritems()])
</span><span class="cx">         totalTime = time.time() - startTime
</span><span class="cx">         self.log.info(&quot;Calendar user search for %s matched %d records (%s) in %.2f seconds&quot; % (tokens, len(records), typeCountsStr, totalTime))
</span><span class="lines">@@ -1166,7 +1167,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         records = []
</span><span class="cx"> 
</span><del>-        self.log.debug(&quot;Peforming principal property search for %s&quot; % (fields,))
</del><ins>+        self.log.debug(&quot;Performing principal property search for %s&quot; % (fields,))
</ins><span class="cx"> 
</span><span class="cx">         if recordType is None:
</span><span class="cx">             # Make a copy since we're modifying it
</span><span class="lines">@@ -1318,6 +1319,7 @@
</span><span class="cx"> 
</span><span class="cx">         returnValue(recordsByAlias.values())
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def recordTypeForDN(self, dnStr):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Examine a DN to determine which recordType it belongs to
</span><span class="lines">@@ -1333,6 +1335,7 @@
</span><span class="cx">         return None
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def dnContainedIn(child, parent):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Return True if child dn is contained within parent dn, otherwise False.
</span><span class="lines">@@ -1340,6 +1343,7 @@
</span><span class="cx">     return child[-len(parent):] == parent
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def normalizeDNstr(dnStr):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Convert to lowercase and remove extra whitespace
</span><span class="lines">@@ -1350,6 +1354,7 @@
</span><span class="cx">     return ' '.join(ldap.dn.dn2str(ldap.dn.str2dn(dnStr.lower())).split())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def _convertValue(value, matchType):
</span><span class="cx">     if matchType == &quot;starts-with&quot;:
</span><span class="cx">         value = &quot;%s*&quot; % (ldapEsc(value),)
</span><span class="lines">@@ -1360,6 +1365,8 @@
</span><span class="cx">         value = ldapEsc(value)
</span><span class="cx">     return value
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> def buildFilter(recordType, mapping, fields, operand=&quot;or&quot;, optimizeMultiName=False):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Create an LDAP filter string from a list of tuples representing directory
</span><span class="lines">@@ -1397,8 +1404,8 @@
</span><span class="cx">                 # try the various firstName/lastName permutations:
</span><span class="cx">                 if recordType == &quot;users&quot;:
</span><span class="cx">                     converted = []
</span><del>-                    for firstName, firstCaseless, firstMatchType in combined[&quot;firstName&quot;]:
-                        for lastName, lastCaseless, lastMatchType in combined[&quot;lastName&quot;]:
</del><ins>+                    for firstName, _ignore_firstCaseless, firstMatchType in combined[&quot;firstName&quot;]:
+                        for lastName, _ignore_lastCaseless, lastMatchType in combined[&quot;lastName&quot;]:
</ins><span class="cx">                             if firstName != lastName:
</span><span class="cx">                                 firstValue = _convertValue(firstName, firstMatchType)
</span><span class="cx">                                 lastValue = _convertValue(lastName, lastMatchType)
</span><span class="lines">@@ -1421,7 +1428,7 @@
</span><span class="cx">         # name, guid)
</span><span class="cx">         additional = []
</span><span class="cx">         for key in (&quot;recordName&quot;, &quot;guid&quot;):
</span><del>-            if mapping.has_key(key):
</del><ins>+            if key in mapping:
</ins><span class="cx">                 additional.append(&quot;(%s=*)&quot; % (mapping.get(key),))
</span><span class="cx">         if additional:
</span><span class="cx">             filterstr = &quot;(&amp;%s%s)&quot; % (&quot;&quot;.join(additional), filterstr)
</span><span class="lines">@@ -1429,6 +1436,7 @@
</span><span class="cx">     return filterstr
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> def buildFilterFromTokens(recordType, mapping, tokens, extra=None):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Create an LDAP filter string from a list of query tokens.  Each token is
</span><span class="lines">@@ -1491,6 +1499,7 @@
</span><span class="cx">     return filterStr
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class LdapDirectoryRecord(CachingDirectoryRecord):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     LDAP implementation of L{IDirectoryRecord}.
</span><span class="lines">@@ -1503,18 +1512,18 @@
</span><span class="cx">         attrs
</span><span class="cx">     ):
</span><span class="cx">         super(LdapDirectoryRecord, self).__init__(
</span><del>-            service               = service,
-            recordType            = recordType,
-            guid                  = guid,
-            shortNames            = shortNames,
-            authIDs               = authIDs,
-            fullName              = fullName,
-            firstName             = firstName,
-            lastName              = lastName,
-            emailAddresses        = emailAddresses,
-            extProxies            = extProxies,
-            extReadOnlyProxies    = extReadOnlyProxies,
-            uid                   = uid,
</del><ins>+            service=service,
+            recordType=recordType,
+            guid=guid,
+            shortNames=shortNames,
+            authIDs=authIDs,
+            fullName=fullName,
+            firstName=firstName,
+            lastName=lastName,
+            emailAddresses=emailAddresses,
+            extProxies=extProxies,
+            extReadOnlyProxies=extReadOnlyProxies,
+            uid=uid,
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         # Save attributes of dn and attrs in case you might need them later
</span><span class="lines">@@ -1542,6 +1551,7 @@
</span><span class="cx">             self._members_storage = self._members()
</span><span class="cx">             return self._members_storage
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _members(self):
</span><span class="cx">         &quot;&quot;&quot; Fault in records for the members of this group &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -1572,7 +1582,7 @@
</span><span class="cx"> 
</span><span class="cx">                 dn, attrs = result.pop()
</span><span class="cx">                 dn = normalizeDNstr(dn)
</span><del>-                self.log.debug(&quot;Retrieved: %s %s&quot; % (dn,attrs))
</del><ins>+                self.log.debug(&quot;Retrieved: %s %s&quot; % (dn, attrs))
</ins><span class="cx">                 recordType = self.service.recordTypeForDN(dn)
</span><span class="cx">                 if recordType is None:
</span><span class="cx">                     self.log.error(&quot;Unable to map %s to a record type&quot; % (dn,))
</span><span class="lines">@@ -1589,6 +1599,7 @@
</span><span class="cx"> 
</span><span class="cx">         return results
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def groups(self):
</span><span class="cx">         &quot;&quot;&quot; Return the records representing groups this record is a member of &quot;&quot;&quot;
</span><span class="cx">         try:
</span><span class="lines">@@ -1597,6 +1608,7 @@
</span><span class="cx">             self._groups_storage = self._groups()
</span><span class="cx">             return self._groups_storage
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _groups(self):
</span><span class="cx">         &quot;&quot;&quot; Fault in the groups of which this record is a member &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -1612,7 +1624,7 @@
</span><span class="cx">         if len(membersAttrs) == 1:
</span><span class="cx">             filterstr = &quot;(%s=%s)&quot; % (membersAttrs[0], self._memberId)
</span><span class="cx">         else:
</span><del>-            filterstr = &quot;(|%s)&quot; % ( &quot;&quot;.join(
</del><ins>+            filterstr = &quot;(|%s)&quot; % (&quot;&quot;.join(
</ins><span class="cx">                     [&quot;(%s=%s)&quot; % (a, self._memberId) for a in membersAttrs]
</span><span class="cx">                 ),
</span><span class="cx">             )
</span><span class="lines">@@ -1635,6 +1647,7 @@
</span><span class="cx"> 
</span><span class="cx">         return groups
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def cachedGroupsAlias(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         See directory.py for full description
</span><span class="lines">@@ -1644,6 +1657,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         return self._memberId
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def memberGUIDs(self):
</span><span class="cx">         return set(self._memberGUIDs)
</span><span class="cx"> 
</span><span class="lines">@@ -1711,10 +1725,13 @@
</span><span class="cx">         return super(LdapDirectoryRecord, self).verifyCredentials(credentials)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class MissingRecordNameException(Exception):
</span><span class="cx">     &quot;&quot;&quot; Raised when LDAP record is missing recordName &quot;&quot;&quot;
</span><span class="cx">     pass
</span><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class MissingGuidException(Exception):
</span><span class="cx">     &quot;&quot;&quot; Raised when LDAP record is missing guidAttr and it's required &quot;&quot;&quot;
</span><span class="cx">     pass
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/principal.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/principal.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/principal.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -601,7 +601,6 @@
</span><span class="cx">         record = self.resource.record
</span><span class="cx">         return tag.fillSlots(
</span><span class="cx">             hostedAt=str(record.serverURI()),
</span><del>-            partition=str(record.effectivePartitionID()),
</del><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1066,14 +1065,6 @@
</span><span class="cx">         return self.record.server()
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def partitionURI(self):
-        return self.record.partitionURI()
-
-
-    def locallyHosted(self):
-        return self.record.locallyHosted()
-
-
</del><span class="cx">     def thisServer(self):
</span><span class="cx">         return self.record.thisServer()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/resource.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/resource.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/resource.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -25,14 +25,13 @@
</span><span class="cx"> __all__ = [&quot;DirectoryReverseProxyResource&quot;]
</span><span class="cx"> 
</span><span class="cx"> class DirectoryReverseProxyResource(ReverseProxyResource):
</span><del>-    
</del><ins>+
</ins><span class="cx">     def __init__(self, parent, record):
</span><span class="cx">         self.parent = parent
</span><span class="cx">         self.record = record
</span><del>-        
-        super(DirectoryReverseProxyResource, self).__init__(self.record.partitionID)
-    
-    def url(self):
-        return joinURL(self.parent.url(), self.record.uid)
</del><span class="cx"> 
</span><ins>+        super(DirectoryReverseProxyResource, self).__init__(self.record.serverID)
</ins><span class="cx"> 
</span><ins>+
+    def url(self):
+        return joinURL(self.parent.url(), self.record.uid)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestaugmentstestdefaultxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test-default.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test-default.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test-default.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -24,40 +24,34 @@
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><del>-    &lt;partition-id&gt;00001&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;Location-Default&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><del>-    &lt;partition-id&gt;00004&lt;/partition-id&gt;
</del><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;Location-AA*&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><del>-    &lt;partition-id&gt;00005&lt;/partition-id&gt;
</del><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;Resource-Default&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><del>-    &lt;partition-id&gt;00006&lt;/partition-id&gt;
</del><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;Resource-AA*&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><del>-    &lt;partition-id&gt;00007&lt;/partition-id&gt;
</del><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;AA*&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00001&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;AB*&lt;/uid&gt;
</span><span class="lines">@@ -66,14 +60,12 @@
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;B*&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00002&lt;/partition-id&gt;
</del><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;C*&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00003&lt;/partition-id&gt;
</del><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span><span class="lines">@@ -102,17 +94,14 @@
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00001&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;543D28BA-F74F-4D5F-9243-B3E3A61171E5&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00002&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;6A73326A-F781-47E7-A9F8-AF47364D4152&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00002&lt;/partition-id&gt;
</del><span class="cx">     &lt;enable-calendar&gt;true&lt;/enable-calendar&gt;
</span><span class="cx">     &lt;enable-addressbook&gt;true&lt;/enable-addressbook&gt;
</span><span class="cx">     &lt;auto-schedule&gt;true&lt;/auto-schedule&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestaugmentstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test.xml (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test.xml        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/augments-test.xml        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -42,12 +42,10 @@
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00001&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;543D28BA-F74F-4D5F-9243-B3E3A61171E5&lt;/uid&gt;
</span><span class="cx">     &lt;enable&gt;true&lt;/enable&gt;
</span><del>-    &lt;partition-id&gt;00002&lt;/partition-id&gt;
</del><span class="cx">   &lt;/record&gt;
</span><span class="cx">   &lt;record&gt;
</span><span class="cx">     &lt;uid&gt;6A73326A-F781-47E7-A9F8-AF47364D4152&lt;/uid&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestresourcescaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/resources/caldavd.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/resources/caldavd.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/resources/caldavd.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -175,7 +175,6 @@
</span><span class="cx">         Augments for the directory service records to add calendar specific attributes.
</span><span class="cx"> 
</span><span class="cx">         A variety of augment services are available for use.
</span><del>-        When using a partitioned server, a service that can be accessed from each host will be needed.
</del><span class="cx">       --&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;!-- XML File Augment Service --&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestsudoersplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,28 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-  &lt;dict&gt;
-    &lt;key&gt;users&lt;/key&gt;
-      &lt;array&gt;
-        &lt;dict&gt;
-          &lt;key&gt;authorize-as&lt;/key&gt;
-          &lt;dict&gt;
-            &lt;key&gt;allow&lt;/key&gt;
-            &lt;true /&gt;
-            &lt;key&gt;principals&lt;/key&gt;
-            &lt;array&gt;
-              &lt;string&gt;all&lt;/string&gt;
-            &lt;/array&gt;
-          &lt;/dict&gt;
-          &lt;key&gt;authorize-from&lt;/key&gt;
-          &lt;array&gt;
-            &lt;string&gt;127.0.0.1&lt;/string&gt;
-          &lt;/array&gt;
-          &lt;key&gt;password&lt;/key&gt;
-          &lt;string&gt;alice&lt;/string&gt;
-          &lt;key&gt;username&lt;/key&gt;
-          &lt;string&gt;alice&lt;/string&gt;
-        &lt;/dict&gt;
-      &lt;/array&gt;
-  &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytestsudoers2plist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers2.plist (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers2.plist        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/sudoers2.plist        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -1,47 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-  &lt;dict&gt;
-    &lt;key&gt;users&lt;/key&gt;
-      &lt;array&gt;
-        &lt;dict&gt;
-          &lt;key&gt;authorize-as&lt;/key&gt;
-          &lt;dict&gt;
-            &lt;key&gt;allow&lt;/key&gt;
-            &lt;true /&gt;
-            &lt;key&gt;principals&lt;/key&gt;
-            &lt;array&gt;
-              &lt;string&gt;all&lt;/string&gt;
-            &lt;/array&gt;
-          &lt;/dict&gt;
-          &lt;key&gt;authorize-from&lt;/key&gt;
-          &lt;array&gt;
-            &lt;string&gt;127.0.0.1&lt;/string&gt;
-          &lt;/array&gt;
-          &lt;key&gt;password&lt;/key&gt;
-          &lt;string&gt;alice&lt;/string&gt;
-          &lt;key&gt;username&lt;/key&gt;
-          &lt;string&gt;alice&lt;/string&gt;
-        &lt;/dict&gt;
-        &lt;dict&gt;
-          &lt;key&gt;authorize-as&lt;/key&gt;
-          &lt;dict&gt;
-            &lt;key&gt;allow&lt;/key&gt;
-            &lt;true /&gt;
-            &lt;key&gt;principals&lt;/key&gt;
-            &lt;array&gt;
-              &lt;string&gt;all&lt;/string&gt;
-            &lt;/array&gt;
-          &lt;/dict&gt;
-          &lt;key&gt;authorize-from&lt;/key&gt;
-          &lt;array&gt;
-            &lt;string&gt;127.0.0.1&lt;/string&gt;
-          &lt;/array&gt;
-          &lt;key&gt;password&lt;/key&gt;
-          &lt;string&gt;bob&lt;/string&gt;
-          &lt;key&gt;username&lt;/key&gt;
-          &lt;string&gt;bob&lt;/string&gt;
-        &lt;/dict&gt;
-      &lt;/array&gt;
-  &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_augmentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_augment.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_augment.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_augment.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -32,46 +32,46 @@
</span><span class="cx"> xmlFileNormalization = os.path.join(os.path.dirname(__file__), &quot;augments-normalization.xml&quot;)
</span><span class="cx"> 
</span><span class="cx"> testRecords = (
</span><del>-    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975766&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;6423F94A-6B76-4A3A-815B-D52CFD77935D&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;5A985493-EE2C-4665-94CF-4DFEA3A89500&quot;, &quot;enabled&quot;: False, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;8B4288F6-CC82-491D-8EF9-642EF4F3E7D0&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00001&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;543D28BA-F74F-4D5F-9243-B3E3A61171E5&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00002&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;6A73326A-F781-47E7-A9F8-AF47364D4152&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00002&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;8AB34DF9-0297-4BA3-AADB-DB557DDD21E7&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
</del><ins>+    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975766&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;6423F94A-6B76-4A3A-815B-D52CFD77935D&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;5A985493-EE2C-4665-94CF-4DFEA3A89500&quot;, &quot;enabled&quot;: False, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;8B4288F6-CC82-491D-8EF9-642EF4F3E7D0&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;543D28BA-F74F-4D5F-9243-B3E3A61171E5&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;6A73326A-F781-47E7-A9F8-AF47364D4152&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;C5BAADEE-6B35-4FD5-A98A-5DF6BBAAC47A&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;8AB34DF9-0297-4BA3-AADB-DB557DDD21E7&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
</ins><span class="cx">     {&quot;uid&quot;: &quot;FC674703-8008-4A77-B80E-0DB55A9CE620&quot;, &quot;enabledForLogin&quot;: False, }, # Explicitly false
</span><span class="cx">     {&quot;uid&quot;: &quot;B473DC32-1B0D-45EE-9BAC-DA878AE9CE74&quot;, &quot;enabledForLogin&quot;: True, }, # Explicitly True
</span><span class="cx">     {&quot;uid&quot;: &quot;9F2B176D-B3F5-483A-AA63-0A1FC6E6D54B&quot;, &quot;enabledForLogin&quot;: True, }, # Default is True
</span><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> testRecordWildcardDefault = (
</span><del>-    {&quot;uid&quot;: &quot;A4318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00001&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;AA5F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00001&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA&quot;, &quot;enabled&quot;: False, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;BC22A734-5E41-4FB7-B5C1-51DC0656DC2F&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00002&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00003&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
-    {&quot;uid&quot;: &quot;AA859321-2C72-4974-ADCF-0CBA0C76F95D&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00001&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;AB7C488B-9ED2-4265-881C-7E2E38A63584&quot;, &quot;enabled&quot;: False, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;BB0C0DA1-0545-45F6-8D08-917C554D93A4&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00002&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
-    {&quot;uid&quot;: &quot;CCD30AD3-582F-4682-8B65-2EDE92C5656E&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00003&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
</del><ins>+    {&quot;uid&quot;: &quot;A4318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;AA5F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;ABF1A83B-1A29-4E04-BDC3-A6A66ECF27CA&quot;, &quot;enabled&quot;: False, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;BC22A734-5E41-4FB7-B5C1-51DC0656DC2F&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;C6DEEBB1-E14A-47F2-98BA-7E3BB4353E3A&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
+    {&quot;uid&quot;: &quot;AA859321-2C72-4974-ADCF-0CBA0C76F95D&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;AB7C488B-9ED2-4265-881C-7E2E38A63584&quot;, &quot;enabled&quot;: False, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;BB0C0DA1-0545-45F6-8D08-917C554D93A4&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
+    {&quot;uid&quot;: &quot;CCD30AD3-582F-4682-8B65-2EDE92C5656E&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;accept-always&quot;},
</ins><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> testRecordTypeDefault = (
</span><del>-    (&quot;locations&quot;, {&quot;uid&quot;: &quot;A4318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00004&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
-    (&quot;locations&quot;, {&quot;uid&quot;: &quot;AA5F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00005&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
-    (&quot;resources&quot;, {&quot;uid&quot;: &quot;A5318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00006&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
-    (&quot;resources&quot;, {&quot;uid&quot;: &quot;AA6F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;00007&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
</del><ins>+    (&quot;locations&quot;, {&quot;uid&quot;: &quot;A4318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
+    (&quot;locations&quot;, {&quot;uid&quot;: &quot;AA5F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
+    (&quot;resources&quot;, {&quot;uid&quot;: &quot;A5318887-F2C7-4A70-9056-B88CC8DB26F1&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
+    (&quot;resources&quot;, {&quot;uid&quot;: &quot;AA6F935F-3358-4510-A649-B391D63279F2&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: True, &quot;autoScheduleMode&quot;: &quot;default&quot;}),
</ins><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> testAddRecords = (
</span><del>-    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975767&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
</del><ins>+    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975767&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: False, &quot;enabledForAddressBooks&quot;: False, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
</ins><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> testModifyRecords = (
</span><del>-    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975767&quot;, &quot;enabled&quot;: True, &quot;partitionID&quot;: &quot;&quot;, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
</del><ins>+    {&quot;uid&quot;: &quot;D11F03A0-97EA-48AF-9A6C-FAC7F3975767&quot;, &quot;enabled&quot;: True, &quot;enabledForCalendaring&quot;: True, &quot;enabledForAddressBooks&quot;: True, &quot;autoSchedule&quot;: False, &quot;autoScheduleMode&quot;: &quot;default&quot;},
</ins><span class="cx"> )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_directory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_directory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_directory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -539,7 +539,167 @@
</span><span class="cx">                 groups,
</span><span class="cx">             )
</span><span class="cx"> 
</span><ins>+        #
+        # Now remove all external assignments, and those should take effect.
+        #
+        def fakeExternalProxiesEmpty():
+            return []
</ins><span class="cx"> 
</span><ins>+        updater = GroupMembershipCacheUpdater(
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
+            cache=cache, useExternalProxies=True,
+            externalProxiesSource=fakeExternalProxiesEmpty)
+
+        yield updater.updateCache()
+
+        delegates = (
+
+            # record name
+            # read-write delegators
+            # read-only delegators
+            # groups delegate is in (restricted to only those groups
+            #   participating in delegation)
+
+            # Note: &quot;transporter&quot; is now gone for everyone
+
+            (&quot;wsanchez&quot;,
+             set([&quot;mercury&quot;, &quot;apollo&quot;, &quot;orion&quot;, &quot;gemini&quot;]),
+             set([&quot;non_calendar_proxy&quot;]),
+             set(['left_coast',
+                  'both_coasts',
+                  'recursive1_coasts',
+                  'recursive2_coasts',
+                  'gemini#calendar-proxy-write',
+                ]),
+            ),
+            (&quot;cdaboo&quot;,
+             set([&quot;apollo&quot;, &quot;orion&quot;, &quot;non_calendar_proxy&quot;]),
+             set([&quot;non_calendar_proxy&quot;]),
+             set(['both_coasts',
+                  'non_calendar_group',
+                  'recursive1_coasts',
+                  'recursive2_coasts',
+                ]),
+            ),
+            (&quot;lecroy&quot;,
+             set([&quot;apollo&quot;, &quot;mercury&quot;, &quot;non_calendar_proxy&quot;]),
+             set(),
+             set(['both_coasts',
+                  'left_coast',
+                      'non_calendar_group',
+                ]),
+            ),
+        )
+
+        for name, write, read, groups in delegates:
+            delegate = self._getPrincipalByShortName(DirectoryService.recordType_users, name)
+
+            proxyFor = (yield delegate.proxyFor(True))
+            self.assertEquals(
+                set([p.record.guid for p in proxyFor]),
+                write,
+            )
+            proxyFor = (yield delegate.proxyFor(False))
+            self.assertEquals(
+                set([p.record.guid for p in proxyFor]),
+                read,
+            )
+            groupsIn = (yield delegate.groupMemberships())
+            uids = set()
+            for group in groupsIn:
+                try:
+                    uid = group.uid # a sub-principal
+                except AttributeError:
+                    uid = group.record.guid # a regular group
+                uids.add(uid)
+            self.assertEquals(
+                set(uids),
+                groups,
+            )
+
+        #
+        # Now add back an external assignments, and those should take effect.
+        #
+        def fakeExternalProxiesAdded():
+            return [
+                (
+                    &quot;transporter#calendar-proxy-write&quot;,
+                    set([&quot;8B4288F6-CC82-491D-8EF9-642EF4F3E7D0&quot;])
+                ),
+            ]
+
+        updater = GroupMembershipCacheUpdater(
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
+            cache=cache, useExternalProxies=True,
+            externalProxiesSource=fakeExternalProxiesAdded)
+
+        yield updater.updateCache()
+
+        delegates = (
+
+            # record name
+            # read-write delegators
+            # read-only delegators
+            # groups delegate is in (restricted to only those groups
+            #   participating in delegation)
+
+            (&quot;wsanchez&quot;,
+             set([&quot;mercury&quot;, &quot;apollo&quot;, &quot;orion&quot;, &quot;gemini&quot;]),
+             set([&quot;non_calendar_proxy&quot;]),
+             set(['left_coast',
+                  'both_coasts',
+                  'recursive1_coasts',
+                  'recursive2_coasts',
+                  'gemini#calendar-proxy-write',
+                ]),
+            ),
+            (&quot;cdaboo&quot;,
+             set([&quot;apollo&quot;, &quot;orion&quot;, &quot;non_calendar_proxy&quot;]),
+             set([&quot;non_calendar_proxy&quot;]),
+             set(['both_coasts',
+                  'non_calendar_group',
+                  'recursive1_coasts',
+                  'recursive2_coasts',
+                ]),
+            ),
+            (&quot;lecroy&quot;,
+             set([&quot;apollo&quot;, &quot;mercury&quot;, &quot;non_calendar_proxy&quot;, &quot;transporter&quot;]),
+             set(),
+             set(['both_coasts',
+                  'left_coast',
+                  'non_calendar_group',
+                  'transporter#calendar-proxy-write',
+                ]),
+            ),
+        )
+
+        for name, write, read, groups in delegates:
+            delegate = self._getPrincipalByShortName(DirectoryService.recordType_users, name)
+
+            proxyFor = (yield delegate.proxyFor(True))
+            self.assertEquals(
+                set([p.record.guid for p in proxyFor]),
+                write,
+            )
+            proxyFor = (yield delegate.proxyFor(False))
+            self.assertEquals(
+                set([p.record.guid for p in proxyFor]),
+                read,
+            )
+            groupsIn = (yield delegate.groupMemberships())
+            uids = set()
+            for group in groupsIn:
+                try:
+                    uid = group.uid # a sub-principal
+                except AttributeError:
+                    uid = group.record.guid # a regular group
+                uids.add(uid)
+            self.assertEquals(
+                set(uids),
+                groups,
+            )
+
+
</ins><span class="cx">     def test_diffAssignments(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Ensure external proxy assignment diffing works
</span><span class="lines">@@ -728,7 +888,7 @@
</span><span class="cx">         }
</span><span class="cx">         members = pickle.loads(snapshotFile.getContent())
</span><span class="cx">         self.assertEquals(members, expected)
</span><del>-        
</del><ins>+
</ins><span class="cx">         # &quot;Corrupt&quot; the snapshot and verify it is regenerated properly
</span><span class="cx">         snapshotFile.setContent(&quot;xyzzy&quot;)
</span><span class="cx">         cache.delete(&quot;group-cacher-populated&quot;)
</span><span class="lines">@@ -739,8 +899,8 @@
</span><span class="cx">         self.assertTrue(snapshotFile.exists())
</span><span class="cx">         members = pickle.loads(snapshotFile.getContent())
</span><span class="cx">         self.assertEquals(members, expected)
</span><del>-        
</del><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def test_autoAcceptMembers(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         autoAcceptMembers( ) returns an empty list if no autoAcceptGroup is
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectorytesttest_ldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_ldapdirectory.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_ldapdirectory.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/test/test_ldapdirectory.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -588,9 +588,8 @@
</span><span class="cx">                     &quot;readOnlyProxyAttr&quot;: &quot;read-only-proxy&quot;,
</span><span class="cx">                     &quot;autoAcceptGroupAttr&quot;: None,
</span><span class="cx">                 },
</span><del>-                &quot;partitionSchema&quot;: {
</del><ins>+                &quot;poddingSchema&quot;: {
</ins><span class="cx">                     &quot;serverIdAttr&quot;: &quot;server-id&quot;, # maps to augments server-id
</span><del>-                    &quot;partitionIdAttr&quot;: &quot;partition-id&quot;, # maps to augments partition-id
</del><span class="cx">                 },
</span><span class="cx">             }
</span><span class="cx">         )
</span><span class="lines">@@ -804,9 +803,8 @@
</span><span class="cx">                     &quot;readOnlyProxyAttr&quot;: None,
</span><span class="cx">                     &quot;autoAcceptGroupAttr&quot;: None,
</span><span class="cx">                 },
</span><del>-                &quot;partitionSchema&quot;: {
</del><ins>+                &quot;poddingSchema&quot;: {
</ins><span class="cx">                     &quot;serverIdAttr&quot;: &quot;server-id&quot;, # maps to augments server-id
</span><del>-                    &quot;partitionIdAttr&quot;: &quot;partition-id&quot;, # maps to augments partition-id
</del><span class="cx">                 },
</span><span class="cx">             }
</span><span class="cx">         )
</span><span class="lines">@@ -1022,9 +1020,8 @@
</span><span class="cx">                     &quot;readOnlyProxyAttr&quot;: None,
</span><span class="cx">                     &quot;autoAcceptGroupAttr&quot;: None,
</span><span class="cx">                 },
</span><del>-                &quot;partitionSchema&quot;: {
</del><ins>+                &quot;poddingSchema&quot;: {
</ins><span class="cx">                     &quot;serverIdAttr&quot;: &quot;server-id&quot;, # maps to augments server-id
</span><del>-                    &quot;partitionIdAttr&quot;: &quot;partition-id&quot;, # maps to augments partition-id
</del><span class="cx">                 },
</span><span class="cx">             }
</span><span class="cx">         )
</span><span class="lines">@@ -1236,9 +1233,8 @@
</span><span class="cx">                     &quot;readOnlyProxyAttr&quot;: None,
</span><span class="cx">                     &quot;autoAcceptGroupAttr&quot;: None,
</span><span class="cx">                 },
</span><del>-                &quot;partitionSchema&quot;: {
</del><ins>+                &quot;poddingSchema&quot;: {
</ins><span class="cx">                     &quot;serverIdAttr&quot;: &quot;server-id&quot;, # maps to augments server-id
</span><del>-                    &quot;partitionIdAttr&quot;: &quot;partition-id&quot;, # maps to augments partition-id
</del><span class="cx">                 },
</span><span class="cx">             }
</span><span class="cx">         )
</span><span class="lines">@@ -1296,7 +1292,6 @@
</span><span class="cx">             self.assertEquals(record.firstName, 'Amanda')
</span><span class="cx">             self.assertEquals(record.lastName, 'Test')
</span><span class="cx">             self.assertEquals(record.serverID, None)
</span><del>-            self.assertEquals(record.partitionID, None)
</del><span class="cx">             self.assertFalse(record.enabledForCalendaring)
</span><span class="cx"> 
</span><span class="cx">             # User with enabled-for-calendaring specified
</span><span class="lines">@@ -1326,13 +1321,11 @@
</span><span class="cx">                 'apple-generateduid': [guid],
</span><span class="cx">                 'cn': ['Amanda Test'],
</span><span class="cx">                 'server-id' : [&quot;test-server-id&quot;],
</span><del>-                'partition-id' : [&quot;test-partition-id&quot;],
</del><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             record = self.service._ldapResultToRecord(dn, attrs,
</span><span class="cx">                 self.service.recordType_users)
</span><span class="cx">             self.assertEquals(record.serverID, &quot;test-server-id&quot;)
</span><del>-            self.assertEquals(record.partitionID, &quot;test-partition-id&quot;)
</del><span class="cx"> 
</span><span class="cx">             # User missing guidAttr
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavdirectoryxmlaugmentsparserpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/xmlaugmentsparser.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/xmlaugmentsparser.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/directory/xmlaugmentsparser.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -30,38 +30,36 @@
</span><span class="cx"> 
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><del>-ELEMENT_AUGMENTS          = &quot;augments&quot;
-ELEMENT_RECORD            = &quot;record&quot;
</del><ins>+ELEMENT_AUGMENTS = &quot;augments&quot;
+ELEMENT_RECORD = &quot;record&quot;
</ins><span class="cx"> 
</span><del>-ELEMENT_UID               = &quot;uid&quot;
-ELEMENT_ENABLE            = &quot;enable&quot;
-ELEMENT_SERVERID          = &quot;server-id&quot;
-ELEMENT_PARTITIONID       = &quot;partition-id&quot;
-ELEMENT_HOSTEDAT          = &quot;hosted-at&quot;   # Backwards compatibility
-ELEMENT_ENABLECALENDAR    = &quot;enable-calendar&quot;
</del><ins>+ELEMENT_UID = &quot;uid&quot;
+ELEMENT_ENABLE = &quot;enable&quot;
+ELEMENT_SERVERID = &quot;server-id&quot;
+ELEMENT_PARTITIONID = &quot;partition-id&quot;   # Backwards compatibility
+ELEMENT_HOSTEDAT = &quot;hosted-at&quot;   # Backwards compatibility
+ELEMENT_ENABLECALENDAR = &quot;enable-calendar&quot;
</ins><span class="cx"> ELEMENT_ENABLEADDRESSBOOK = &quot;enable-addressbook&quot;
</span><del>-ELEMENT_ENABLELOGIN       = &quot;enable-login&quot;
-ELEMENT_AUTOSCHEDULE      = &quot;auto-schedule&quot;
</del><ins>+ELEMENT_ENABLELOGIN = &quot;enable-login&quot;
+ELEMENT_AUTOSCHEDULE = &quot;auto-schedule&quot;
</ins><span class="cx"> ELEMENT_AUTOSCHEDULE_MODE = &quot;auto-schedule-mode&quot;
</span><del>-ELEMENT_AUTOACCEPTGROUP   = &quot;auto-accept-group&quot;
</del><ins>+ELEMENT_AUTOACCEPTGROUP = &quot;auto-accept-group&quot;
</ins><span class="cx"> 
</span><del>-ATTRIBUTE_REPEAT          = &quot;repeat&quot;
</del><ins>+ATTRIBUTE_REPEAT = &quot;repeat&quot;
</ins><span class="cx"> 
</span><del>-VALUE_TRUE                = &quot;true&quot;
-VALUE_FALSE               = &quot;false&quot;
</del><ins>+VALUE_TRUE = &quot;true&quot;
+VALUE_FALSE = &quot;false&quot;
</ins><span class="cx"> 
</span><span class="cx"> ELEMENT_AUGMENTRECORD_MAP = {
</span><del>-    ELEMENT_UID:               &quot;uid&quot;,
-    ELEMENT_ENABLE:            &quot;enabled&quot;,
-    ELEMENT_SERVERID:          &quot;serverID&quot;,
-    ELEMENT_PARTITIONID:       &quot;partitionID&quot;,
-    ELEMENT_HOSTEDAT:          &quot;partitionID&quot;,   # Backwards compatibility
-    ELEMENT_ENABLECALENDAR:    &quot;enabledForCalendaring&quot;,
</del><ins>+    ELEMENT_UID: &quot;uid&quot;,
+    ELEMENT_ENABLE: &quot;enabled&quot;,
+    ELEMENT_SERVERID: &quot;serverID&quot;,
+    ELEMENT_ENABLECALENDAR: &quot;enabledForCalendaring&quot;,
</ins><span class="cx">     ELEMENT_ENABLEADDRESSBOOK: &quot;enabledForAddressBooks&quot;,
</span><del>-    ELEMENT_ENABLELOGIN:       &quot;enabledForLogin&quot;,
-    ELEMENT_AUTOSCHEDULE:      &quot;autoSchedule&quot;,
</del><ins>+    ELEMENT_ENABLELOGIN: &quot;enabledForLogin&quot;,
+    ELEMENT_AUTOSCHEDULE: &quot;autoSchedule&quot;,
</ins><span class="cx">     ELEMENT_AUTOSCHEDULE_MODE: &quot;autoScheduleMode&quot;,
</span><del>-    ELEMENT_AUTOACCEPTGROUP:   &quot;autoAcceptGroup&quot;,
</del><ins>+    ELEMENT_AUTOACCEPTGROUP: &quot;autoAcceptGroup&quot;,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> class XMLAugmentsParser(object):
</span><span class="lines">@@ -71,6 +69,7 @@
</span><span class="cx">     def __repr__(self):
</span><span class="cx">         return &quot;&lt;%s %r&gt;&quot; % (self.__class__.__name__, self.xmlFile)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def __init__(self, xmlFile, items):
</span><span class="cx"> 
</span><span class="cx">         self.items = items
</span><span class="lines">@@ -84,13 +83,14 @@
</span><span class="cx"> 
</span><span class="cx">         self._parseXML(augments_node)
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def _parseXML(self, rootnode):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Parse the XML root node from the augments configuration document.
</span><span class="cx">         @param rootnode: the L{Element} to parse.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for child in rootnode:
</span><del>-            
</del><ins>+
</ins><span class="cx">             if child.tag != ELEMENT_RECORD:
</span><span class="cx">                 raise RuntimeError(&quot;Unknown augment type: '%s' in augment file: '%s'&quot; % (child.tag, self.xmlFile,))
</span><span class="cx"> 
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx"> 
</span><span class="cx">             fields = {}
</span><span class="cx">             for node in child:
</span><del>-                
</del><ins>+
</ins><span class="cx">                 if node.tag in (
</span><span class="cx">                     ELEMENT_UID,
</span><span class="cx">                     ELEMENT_SERVERID,
</span><span class="lines">@@ -118,33 +118,35 @@
</span><span class="cx">                     fields[node.tag] = node.text == VALUE_TRUE
</span><span class="cx">                 else:
</span><span class="cx">                     raise RuntimeError(&quot;Invalid element '%s' in augment file: '%s'&quot; % (node.tag, self.xmlFile,))
</span><del>-                    
</del><ins>+
</ins><span class="cx">             # Must have at least a uid
</span><span class="cx">             if ELEMENT_UID not in fields:
</span><span class="cx">                 raise RuntimeError(&quot;Invalid record '%s' without a uid in augment file: '%s'&quot; % (child, self.xmlFile,))
</span><del>-                
</del><ins>+
</ins><span class="cx">             if repeat &gt; 1:
</span><del>-                for i in xrange(1, repeat+1):
</del><ins>+                for i in xrange(1, repeat + 1):
</ins><span class="cx">                     self.buildRecord(fields, i)
</span><span class="cx">             else:
</span><span class="cx">                 self.buildRecord(fields)
</span><del>-    
</del><ins>+
+
</ins><span class="cx">     def buildRecord(self, fields, count=None):
</span><del>-        
</del><ins>+
</ins><span class="cx">         from twistedcaldav.directory.augment import AugmentRecord
</span><span class="cx"> 
</span><span class="cx">         def expandCount(value, count):
</span><del>-            
</del><ins>+
</ins><span class="cx">             if type(value) in types.StringTypes:
</span><span class="cx">                 return value % (count,) if count and &quot;%&quot; in value else value
</span><span class="cx">             elif type(value) == set:
</span><span class="cx">                 return set([item % (count,) if count and &quot;%&quot; in item else item for item in value])
</span><span class="cx">             else:
</span><span class="cx">                 return value
</span><del>-        
</del><ins>+
</ins><span class="cx">         actualFields = {}
</span><del>-        for k,v in fields.iteritems():
-            actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
</del><ins>+        for k, v in fields.iteritems():
+            if k in ELEMENT_AUGMENTRECORD_MAP:
+                actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
</ins><span class="cx"> 
</span><span class="cx">         record = AugmentRecord(**actualFields)
</span><span class="cx">         self.items[record.uid] = record
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavextensionspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/extensions.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/extensions.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/extensions.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -494,13 +494,13 @@
</span><span class="cx">             size = child.contentLength()
</span><span class="cx">             lastModified = child.lastModified()
</span><span class="cx">             rtypes = []
</span><del>-            fullrtype = child.resourceType()
</del><ins>+            fullrtype = child.resourceType() if hasattr(child, &quot;resourceType&quot;) else None
</ins><span class="cx">             if fullrtype is not None:
</span><span class="cx">                 for rtype in fullrtype.children:
</span><span class="cx">                     rtypes.append(rtype.name)
</span><span class="cx">             if rtypes:
</span><span class="cx">                 rtypes = &quot;(%s)&quot; % (&quot;, &quot;.join(rtypes),)
</span><del>-            if child.isCollection():
</del><ins>+            if child.isCollection() if hasattr(child, &quot;isCollection&quot;) else False:
</ins><span class="cx">                 contentType = rtypes
</span><span class="cx">             else:
</span><span class="cx">                 mimeType = child.contentType()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/ical.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/ical.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/ical.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -63,9 +63,7 @@
</span><span class="cx"> iCalendarProductID = &quot;-//CALENDARSERVER.ORG//NONSGML Version 1//EN&quot;
</span><span class="cx"> 
</span><span class="cx"> allowedStoreComponents = (
</span><del>-    &quot;VEVENT&quot;,
-    &quot;VTODO&quot;,
-    &quot;VPOLL&quot;,
</del><ins>+    &quot;VEVENT&quot;, &quot;VTODO&quot;, &quot;VPOLL&quot;,
</ins><span class="cx"> )
</span><span class="cx"> allowedSchedulingComponents = allowedStoreComponents + (&quot;VFREEBUSY&quot;,)
</span><span class="cx"> allowedComponents = allowedSchedulingComponents + (&quot;VTIMEZONE&quot;,)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/resource.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/resource.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/resource.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -334,6 +334,12 @@
</span><span class="cx">             else:
</span><span class="cx">                 yield transaction.commit()
</span><span class="cx"> 
</span><ins>+                # Log extended item
+                if transaction.logItems:
+                    if not hasattr(request, &quot;extendedLogItems&quot;):
+                        request.extendedLogItems = {}
+                    request.extendedLogItems.update(transaction.logItems)
+
</ins><span class="cx">                 # May need to reset the last-modified header in the response as txn.commit() can change it due to pre-commit hooks
</span><span class="cx">                 if response.headers.hasHeader(&quot;last-modified&quot;):
</span><span class="cx">                     response.headers.setHeader(&quot;last-modified&quot;, self.lastModified())
</span><span class="lines">@@ -2559,15 +2565,6 @@
</span><span class="cx">         return self._newStoreHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object._newStoreObject, mode)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def getCalendarResourcesForUID(self, uid, allow_shared=False):
-        &quot;&quot;&quot;
-        Return all child object resources with the specified UID.
-
-        Pass through direct to store.
-        &quot;&quot;&quot;
-        return self._newStoreHome.getCalendarResourcesForUID(uid, allow_shared)
-
-
</del><span class="cx">     def defaultAccessControlList(self):
</span><span class="cx">         myPrincipal = self.principalForRecord()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/scheduling_store/caldav/resource.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/scheduling_store/caldav/resource.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/scheduling_store/caldav/resource.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -428,8 +428,12 @@
</span><span class="cx">                 authz = (yield request.locateResource(principalURL))
</span><span class="cx">                 self._associatedTransaction._authz_uid = authz.record.guid
</span><span class="cx"> 
</span><ins>+        # Log extended item
+        if not hasattr(request, &quot;extendedLogItems&quot;):
+            request.extendedLogItems = {}
+
</ins><span class="cx">         # This is a local CALDAV scheduling operation.
</span><del>-        scheduler = CalDAVScheduler(self._associatedTransaction, self.parent._newStoreHome.uid())
</del><ins>+        scheduler = CalDAVScheduler(self._associatedTransaction, self.parent._newStoreHome.uid(), logItems=request.extendedLogItems)
</ins><span class="cx"> 
</span><span class="cx">         # Do the POST processing treating
</span><span class="cx">         result = (yield scheduler.doSchedulingViaPOST(originator, recipients, calendar))
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/twistedcaldav/stdconfig.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/twistedcaldav/stdconfig.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/twistedcaldav/stdconfig.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -156,9 +156,8 @@
</span><span class="cx">             &quot;resourceInfoAttr&quot;: None, # contains location/resource info
</span><span class="cx">             &quot;autoAcceptGroupAttr&quot;: None, # auto accept group
</span><span class="cx">         },
</span><del>-        &quot;partitionSchema&quot;: {
</del><ins>+        &quot;poddingSchema&quot;: {
</ins><span class="cx">             &quot;serverIdAttr&quot;: None, # maps to augments server-id
</span><del>-            &quot;partitionIdAttr&quot;: None, # maps to augments partition-id
</del><span class="cx">         },
</span><span class="cx">     },
</span><span class="cx"> }
</span><span class="lines">@@ -307,9 +306,15 @@
</span><span class="cx">     &quot;FailIfUpgradeNeeded&quot;  : True, # Set to True to prevent the server or utility tools
</span><span class="cx">                                    # tools from running if the database needs a schema
</span><span class="cx">                                    # upgrade.
</span><del>-    &quot;StopAfterUpgradeTriggerFile&quot; : &quot;stop_after_upgrade&quot;, # if this file exists
-        # in ConfigRoot, stop the service after finishing upgrade phase
</del><ins>+    &quot;StopAfterUpgradeTriggerFile&quot; : &quot;stop_after_upgrade&quot;,   # if this file exists in ConfigRoot, stop
+                                                            # the service after finishing upgrade phase
</ins><span class="cx"> 
</span><ins>+    &quot;UpgradeHomePrefix&quot;    : &quot;&quot;,    # When upgrading, only upgrade homes where the owner UID starts with
+                                    # with the specified prefix. The upgrade will only be partial and only
+                                    # apply to upgrade pieces that affect entire homes. The upgrade will
+                                    # need to be run again without this prefix set to complete the overall
+                                    # upgrade.
+
</ins><span class="cx">     #
</span><span class="cx">     # Types of service provided
</span><span class="cx">     #
</span><span class="lines">@@ -569,8 +574,8 @@
</span><span class="cx">         }
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    &quot;EnableTimezonesByReference&quot; : False, # Strip out VTIMEZONES that are known
-    &quot;UsePackageTimezones&quot; : False, # Use timezone data from twistedcaldav.zoneinfo - don't copy to Data directory
</del><ins>+    &quot;EnableTimezonesByReference&quot; : True, # Strip out VTIMEZONES that are known
+    &quot;UsePackageTimezones&quot;        : False, # Use timezone data from twistedcaldav.zoneinfo - don't copy to Data directory
</ins><span class="cx"> 
</span><span class="cx">     &quot;EnableBatchUpload&quot;       : True, # POST batch uploads
</span><span class="cx">     &quot;MaxResourcesBatchUpload&quot; : 100, # Maximum number of resources in a batch POST
</span><span class="lines">@@ -811,11 +816,11 @@
</span><span class="cx">     # Support multiple hosts within a domain
</span><span class="cx">     #
</span><span class="cx">     &quot;Servers&quot; : {
</span><del>-        &quot;Enabled&quot;: False, # Multiple servers/partitions enabled or not
-        &quot;ConfigFile&quot;: &quot;localservers.xml&quot;, # File path for server information
-        &quot;MaxClients&quot;: 5, # Pool size for connections to each partition
</del><ins>+        &quot;Enabled&quot;: False,                   # Multiple servers enabled or not
+        &quot;ConfigFile&quot;: &quot;localservers.xml&quot;,   # File path for server information
+        &quot;MaxClients&quot;: 5,                    # Pool size for connections to between servers
+        &quot;InboxName&quot;: &quot;podding&quot;,             # Name for top-level inbox resource
</ins><span class="cx">     },
</span><del>-    &quot;ServerPartitionID&quot;: &quot;&quot;, # Unique ID for this server's partition instance.
</del><span class="cx"> 
</span><span class="cx">     #
</span><span class="cx">     # Performance tuning
</span><span class="lines">@@ -1016,7 +1021,8 @@
</span><span class="cx">     # means no automatic shutdown.
</span><span class="cx">     &quot;AgentInactivityTimeoutSeconds&quot;  : 4 * 60 * 60,
</span><span class="cx"> 
</span><del>-    # These two aren't relative to ConfigRoot:
</del><ins>+    # These aren't relative to ConfigRoot:
+    &quot;ImportConfig&quot;: &quot;&quot;, # Config to read first and merge
</ins><span class="cx">     &quot;Includes&quot;: [], # Other plists to parse after this one
</span><span class="cx">     &quot;WritableConfigFile&quot; : &quot;&quot;, # which config file calendarserver_config should
</span><span class="cx">         # write to for changes; empty string means the main config file.
</span><span class="lines">@@ -1048,18 +1054,41 @@
</span><span class="cx">         if self._configFileName:
</span><span class="cx">             configDict = self._parseConfigFromFile(self._configFileName)
</span><span class="cx">         configDict = ConfigDict(configDict)
</span><del>-        # Now check for Includes and parse and add each of those
-        if &quot;Includes&quot; in configDict:
-            for include in configDict.Includes:
-                # Includes are not relative to ConfigRoot
-                path = _expandPath(include)
</del><ins>+
+        def _loadImport(childDict):
+            # Look for an import and read that one as the main config and merge the current one into that
+            if &quot;ImportConfig&quot; in childDict and childDict.ImportConfig:
+                configRoot = os.path.join(childDict.ServerRoot, childDict.ConfigRoot)
+                path = _expandPath(fullServerPath(configRoot, childDict.ImportConfig))
</ins><span class="cx">                 if os.path.exists(path):
</span><del>-                    additionalDict = ConfigDict(self._parseConfigFromFile(path))
-                    if additionalDict:
-                        log.info(&quot;Adding configuration from file: '%s'&quot; % (path,))
-                        mergeData(configDict, additionalDict)
-                else:
-                    log.debug(&quot;Missing configuration file: '%s'&quot; % (path,))
</del><ins>+                    importDict = ConfigDict(self._parseConfigFromFile(path))
+                    if importDict:
+                        self.importedFiles.append(path)
+                        importDict = _loadImport(importDict)
+                        mergeData(importDict, childDict)
+                        return importDict
+                raise ConfigurationError(&quot;Import configuration file '{path}' must exist and be valid.&quot;.format(path=path))
+            else:
+                return childDict
+
+        def _loadIncludes(parentDict):
+            # Now check for Includes and parse and add each of those
+            if &quot;Includes&quot; in parentDict:
+                configRoot = os.path.join(parentDict.ServerRoot, parentDict.ConfigRoot)
+                for include in parentDict.Includes:
+                    # Includes are not relative to ConfigRoot
+                    path = _expandPath(fullServerPath(configRoot, include))
+                    if os.path.exists(path):
+                        additionalDict = ConfigDict(self._parseConfigFromFile(path))
+                        if additionalDict:
+                            self.includedFiles.append(path)
+                            _loadIncludes(additionalDict)
+                            mergeData(parentDict, additionalDict)
+                    else:
+                        self.missingFiles.append(path)
+
+        configDict = _loadImport(configDict)
+        _loadIncludes(configDict)
</ins><span class="cx">         return configDict
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1484,6 +1513,15 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+def _updateICalendar(configDict, reloading=False):
+    &quot;&quot;&quot;
+    Updated support iCalendar components.
+    &quot;&quot;&quot;
+    from twistedcaldav import ical
+    ical.allowedStoreComponents = tuple(configDict.SupportedComponents)
+
+
+
</ins><span class="cx"> def _updateScheduling(configDict, reloading=False):
</span><span class="cx">     #
</span><span class="cx">     # Scheduling
</span><span class="lines">@@ -1523,8 +1561,7 @@
</span><span class="cx">     from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
</span><span class="cx">     if configDict.Servers.Enabled:
</span><span class="cx">         Servers.load()
</span><del>-        Servers.getThisServer().installReverseProxies(
-            configDict.ServerPartitionID,
</del><ins>+        Servers.installReverseProxies(
</ins><span class="cx">             configDict.Servers.MaxClients,
</span><span class="cx">         )
</span><span class="cx">     else:
</span><span class="lines">@@ -1595,6 +1632,7 @@
</span><span class="cx">     _updateRejectClients,
</span><span class="cx">     _updateLogLevels,
</span><span class="cx">     _updateNotifications,
</span><ins>+    _updateICalendar,
</ins><span class="cx">     _updateScheduling,
</span><span class="cx">     _updateServers,
</span><span class="cx">     _updateCompliance,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavbasedatastoresubpostgrespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/subpostgres.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/subpostgres.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/subpostgres.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -454,6 +454,10 @@
</span><span class="cx">             self.deactivateDelayedShutdown()
</span><span class="cx"> 
</span><span class="cx">         def gotReady(result):
</span><ins>+            &quot;&quot;&quot;
+            We started postgres; we're responsible for stopping it later.
+            Call pgCtl status to get the pid.
+            &quot;&quot;&quot;
</ins><span class="cx">             log.warn(&quot;{cmd} exited&quot;, cmd=pgCtl)
</span><span class="cx">             self.shouldStopDatabase = True
</span><span class="cx">             d = Deferred()
</span><span class="lines">@@ -465,13 +469,32 @@
</span><span class="cx">             )
</span><span class="cx">             return d.addCallback(gotStatus)
</span><span class="cx"> 
</span><del>-        def reportit(f):
-            log.failure(&quot;starting postgres&quot;, f)
</del><ins>+        def couldNotStart(f):
+            &quot;&quot;&quot;
+            There was an error trying to start postgres.  Try to connect
+            because it might already be running.  In this case, we won't
+            be the one to stop it.
+            &quot;&quot;&quot;
+            d = Deferred()
+            statusMonitor = CapturingProcessProtocol(d, None)
+            self.reactor.spawnProcess(
+                statusMonitor, pgCtl, [pgCtl, &quot;status&quot;],
+                env=self.env, path=self.workingDir.path,
+                uid=self.uid, gid=self.gid,
+            )
+            return d.addCallback(gotStatus).addErrback(giveUp)
+
+        def giveUp(f):
+            &quot;&quot;&quot;
+            We can't start postgres or connect to a running instance.  Shut
+            down.
+            &quot;&quot;&quot;
+            log.failure(&quot;Can't start or connect to postgres&quot;, f)
</ins><span class="cx">             self.deactivateDelayedShutdown()
</span><span class="cx">             self.reactor.stop()
</span><span class="cx"> 
</span><span class="cx">         self.monitor.completionDeferred.addCallback(
</span><del>-            gotReady).addErrback(reportit)
</del><ins>+            gotReady).addErrback(couldNotStart)
</ins><span class="cx"> 
</span><span class="cx">     shouldStopDatabase = False
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavbasedatastoreutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/base/datastore/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -92,6 +92,12 @@
</span><span class="cx">         return &quot;objectWithName:%s:%s&quot; % (homeResourceID, name)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    # Home child objects by id
+
+    def keyForObjectWithResourceID(self, homeResourceID, resourceID):
+        return &quot;objectWithName:%s:%s&quot; % (homeResourceID, resourceID)
+
+
</ins><span class="cx">     # Home metadata (Created/Modified)
</span><span class="cx"> 
</span><span class="cx">     def keyForHomeMetaData(self, homeResourceID):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/file.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/file.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/file.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -136,7 +136,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def hasCalendarResourceUIDSomewhereElse(self, uid, ok_object, type):
</span><span class="cx"> 
</span><del>-        objectResources = (yield self.objectResourcesWithUID(uid, (&quot;inbox&quot;,)))
</del><ins>+        objectResources = (yield self.getCalendarResourcesForUID(uid))
</ins><span class="cx">         for objectResource in objectResources:
</span><span class="cx">             if ok_object and objectResource._path == ok_object._path:
</span><span class="cx">                 continue
</span><span class="lines">@@ -148,14 +148,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def getCalendarResourcesForUID(self, uid, allow_shared=False):
</del><ins>+    def getCalendarResourcesForUID(self, uid):
</ins><span class="cx"> 
</span><del>-        results = []
-        objectResources = (yield self.objectResourcesWithUID(uid, (&quot;inbox&quot;,)))
-        for objectResource in objectResources:
-            if allow_shared or objectResource._parentCollection.owned():
-                results.append(objectResource)
-
</del><ins>+        results = (yield self.objectResourcesWithUID(uid, (&quot;inbox&quot;,)))
</ins><span class="cx">         returnValue(results)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/schedule.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/schedule.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/schedule.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -112,8 +112,8 @@
</span><span class="cx">         return self._calendarHome.hasCalendarResourceUIDSomewhereElse(uid, ok_object, type)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def getCalendarResourcesForUID(self, uid, allow_shared=False):
-        return self._calendarHome.getCalendarResourcesForUID(uid, allow_shared)
</del><ins>+    def getCalendarResourcesForUID(self, uid):
+        return self._calendarHome.getCalendarResourcesForUID(uid)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingaddressmappingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/addressmapping.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/addressmapping.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/addressmapping.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -53,7 +53,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def getCalendarUser(self, cuaddr, principal):
</span><span class="cx"> 
</span><del>-        # If we have a principal always treat the user as local or partitioned
</del><ins>+        # If we have a principal always treat the user as local
</ins><span class="cx">         if principal:
</span><span class="cx">             returnValue(calendarUserFromPrincipal(cuaddr, principal))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcaldavdeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/delivery.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/delivery.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/delivery.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -26,8 +26,7 @@
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> 
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, \
-    PartitionedCalendarUser, OtherServerCalendarUser
</del><ins>+from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser, RemoteCalendarUser, OtherServerCalendarUser
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.delivery import DeliveryService
</span><span class="cx"> from txdav.caldav.datastore.scheduling.freebusy import processAvailabilityFreeBusy, \
</span><span class="cx">     generateFreeBusyInfo, buildFreeBusyResult
</span><span class="lines">@@ -99,7 +98,7 @@
</span><span class="cx">         uid = self.scheduler.calendar.resourceUID()
</span><span class="cx"> 
</span><span class="cx">         organizerPrincipal = None
</span><del>-        if type(self.scheduler.organizer) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,):
</del><ins>+        if type(self.scheduler.organizer) in (LocalCalendarUser, OtherServerCalendarUser,):
</ins><span class="cx">             organizerPrincipal = self.scheduler.organizer.principal.uid
</span><span class="cx"> 
</span><span class="cx">         for recipient in self.recipients:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcaldavschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/scheduler.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/caldav/scheduler.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -101,6 +101,14 @@
</span><span class="cx">                 &quot;No principal for originator&quot;,
</span><span class="cx">             ))
</span><span class="cx">         else:
</span><ins>+            if not (originatorPrincipal.calendarsEnabled() and originatorPrincipal.thisServer()):
+                log.error(&quot;Originator not enabled or hosted on this server: %s&quot; % (self.originator,))
+                raise HTTPError(self.errorResponse(
+                    responsecode.FORBIDDEN,
+                    self.errorElements[&quot;originator-denied&quot;],
+                    &quot;Originator cannot be scheduled&quot;,
+                ))
+
</ins><span class="cx">             self.originator = LocalCalendarUser(self.originator, originatorPrincipal)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -127,8 +135,8 @@
</span><span class="cx">             else:
</span><span class="cx">                 # Map recipient to their inbox
</span><span class="cx">                 inbox = None
</span><del>-                if principal.calendarsEnabled() and principal.thisServer():
-                    if principal.locallyHosted():
</del><ins>+                if principal.calendarsEnabled():
+                    if principal.thisServer():
</ins><span class="cx">                         recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
</span><span class="cx">                         if recipient_home:
</span><span class="cx">                             inbox = (yield recipient_home.calendarWithName(&quot;inbox&quot;))
</span><span class="lines">@@ -138,7 +146,7 @@
</span><span class="cx">                 if inbox:
</span><span class="cx">                     results.append(calendarUserFromPrincipal(recipient, principal, inbox))
</span><span class="cx">                 else:
</span><del>-                    log.error(&quot;No schedule inbox for principal: %s&quot; % (principal,))
</del><ins>+                    log.error(&quot;Recipient not enabled for calendaring: %s&quot; % (principal,))
</ins><span class="cx">                     results.append(InvalidCalendarUser(recipient))
</span><span class="cx"> 
</span><span class="cx">         self.recipients = results
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingcuaddresspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/cuaddress.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/cuaddress.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/cuaddress.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -21,7 +21,6 @@
</span><span class="cx"> 
</span><span class="cx"> __all__ = [
</span><span class="cx">     &quot;LocalCalendarUser&quot;,
</span><del>-    &quot;PartitionedCalendarUser&quot;,
</del><span class="cx">     &quot;OtherServerCalendarUser&quot;,
</span><span class="cx">     &quot;RemoteCalendarUser&quot;,
</span><span class="cx">     &quot;EmailCalendarUser&quot;,
</span><span class="lines">@@ -53,19 +52,6 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class PartitionedCalendarUser(CalendarUser):
-
-    def __init__(self, cuaddr, principal):
-        self.cuaddr = cuaddr
-        self.principal = principal
-        self.serviceType = DeliveryService.serviceType_ischedule
-
-
-    def __str__(self):
-        return &quot;Partitioned calendar user: %s&quot; % (self.cuaddr,)
-
-
-
</del><span class="cx"> class OtherServerCalendarUser(CalendarUser):
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, cuaddr, principal):
</span><span class="lines">@@ -145,9 +131,7 @@
</span><span class="cx">     Get the appropriate calendar user address class for the provided principal.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    if principal.locallyHosted():
</del><ins>+    if principal.thisServer():
</ins><span class="cx">         return LocalCalendarUser(recipient, principal, inbox)
</span><del>-    elif principal.thisServer():
-        return PartitionedCalendarUser(recipient, principal)
</del><span class="cx">     else:
</span><span class="cx">         return OtherServerCalendarUser(recipient, principal)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingimplicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/implicit.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/implicit.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/implicit.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -28,7 +28,7 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling import addressmapping
</span><span class="cx"> from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import InvalidCalendarUser, \
</span><del>-    LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser, \
</del><ins>+    LocalCalendarUser, OtherServerCalendarUser, \
</ins><span class="cx">     normalizeCUAddr
</span><span class="cx"> from txdav.caldav.datastore.scheduling.icaldiff import iCalDiff
</span><span class="cx"> from txdav.caldav.datastore.scheduling.itip import iTipGenerator, iTIPRequestStatus
</span><span class="lines">@@ -55,10 +55,10 @@
</span><span class="cx">     STATUS_ORPHANED_CANCELLED_EVENT = 1
</span><span class="cx">     STATUS_ORPHANED_EVENT = 2
</span><span class="cx"> 
</span><del>-    def __init__(self):
</del><ins>+    def __init__(self, logItems=None):
</ins><span class="cx"> 
</span><span class="cx">         self.return_status = ImplicitScheduler.STATUS_OK
</span><del>-        self.logItems = {}
</del><ins>+        self.logItems = logItems
</ins><span class="cx">         self.allowed_to_schedule = True
</span><span class="cx">         self.suppress_refresh = False
</span><span class="cx"> 
</span><span class="lines">@@ -250,7 +250,7 @@
</span><span class="cx">         # to create new scheduling resources.
</span><span class="cx">         if self.action == &quot;create&quot;:
</span><span class="cx">             if self.organizerPrincipal and not self.organizerPrincipal.enabledAsOrganizer():
</span><del>-                log.error(&quot;ORGANIZER not allowed to be an Organizer: %s&quot; % (self.organizer,))
</del><ins>+                log.error(&quot;ORGANIZER not allowed to be an Organizer: {organizer}&quot;, organizer=self.organizer)
</ins><span class="cx">                 raise HTTPError(ErrorResponse(
</span><span class="cx">                     responsecode.FORBIDDEN,
</span><span class="cx">                     (caldav_namespace, &quot;organizer-allowed&quot;),
</span><span class="lines">@@ -382,7 +382,7 @@
</span><span class="cx">             if self.txn.doing_attendee_refresh == 0:
</span><span class="cx">                 delattr(self.txn, &quot;doing_attendee_refresh&quot;)
</span><span class="cx"> 
</span><del>-        if refreshCount:
</del><ins>+        if refreshCount and self.logItems is not None:
</ins><span class="cx">             self.logItems[&quot;itip.refreshes&quot;] = refreshCount
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -426,7 +426,7 @@
</span><span class="cx">             self.organizer = self.calendar.validOrganizerForScheduling()
</span><span class="cx">         except ValueError:
</span><span class="cx">             # We have different ORGANIZERs in the same iCalendar object - this is an error
</span><del>-            log.error(&quot;Only one ORGANIZER is allowed in an iCalendar object:\n%s&quot; % (self.calendar,))
</del><ins>+            log.error(&quot;Only one ORGANIZER is allowed in an iCalendar object:\n{calendar}&quot;, calendar=self.calendar)
</ins><span class="cx">             raise HTTPError(ErrorResponse(
</span><span class="cx">                 responsecode.FORBIDDEN,
</span><span class="cx">                 (caldav_namespace, &quot;single-organizer&quot;),
</span><span class="lines">@@ -461,7 +461,7 @@
</span><span class="cx">         # Check for matching resource somewhere else in the home
</span><span class="cx">         foundElsewhere = (yield self.calendar_home.hasCalendarResourceUIDSomewhereElse(self.uid, check_resource, mode))
</span><span class="cx">         if foundElsewhere is not None:
</span><del>-            log.debug(&quot;Implicit - found component with same UID in a different collection: %s&quot; % (check_resource,))
</del><ins>+            log.debug(&quot;Implicit - found component with same UID in a different collection: {resource}&quot;, resource=check_resource)
</ins><span class="cx">             raise HTTPError(ErrorResponse(
</span><span class="cx">                 responsecode.FORBIDDEN,
</span><span class="cx">                 (caldav_namespace, &quot;unique-scheduling-object-resource&quot;),
</span><span class="lines">@@ -530,7 +530,7 @@
</span><span class="cx">         # Check for a delete
</span><span class="cx">         if self.action == &quot;remove&quot;:
</span><span class="cx"> 
</span><del>-            log.debug(&quot;Implicit - organizer '%s' is removing UID: '%s'&quot; % (self.organizer, self.uid))
</del><ins>+            log.debug(&quot;Implicit - organizer '{organizer}' is removing UID: '{uid}'&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx">             self.oldcalendar = self.calendar
</span><span class="cx"> 
</span><span class="cx">             # Cancel all attendees
</span><span class="lines">@@ -556,16 +556,16 @@
</span><span class="cx">             no_change, self.changed_rids, self.needs_action_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
</span><span class="cx">             if no_change:
</span><span class="cx">                 if reinvites:
</span><del>-                    log.debug(&quot;Implicit - organizer '%s' is re-inviting UID: '%s', attendees: %s&quot; % (self.organizer, self.uid, &quot;, &quot;.join(reinvites)))
</del><ins>+                    log.debug(&quot;Implicit - organizer '{organizer}' is re-inviting UID: '{uid}', attendees: {attendees}&quot;, organizer=self.organizer, uid=self.uid, attendees=&quot;, &quot;.join(reinvites))
</ins><span class="cx">                     self.reinvites = reinvites
</span><span class="cx">                 else:
</span><span class="cx">                     # Nothing to do
</span><del>-                    log.debug(&quot;Implicit - organizer '%s' is modifying UID: '%s' but change is not significant&quot; % (self.organizer, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - organizer '{organizer}' is modifying UID: '{uid}' but change is not significant&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx">                     returnValue(None)
</span><span class="cx">             else:
</span><span class="cx">                 # Do not change PARTSTATs for a split operation
</span><span class="cx">                 if self.split_details is None:
</span><del>-                    log.debug(&quot;Implicit - organizer '%s' is modifying UID: '%s'&quot; % (self.organizer, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - organizer '{organizer}' is modifying UID: '{uid}'&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> 
</span><span class="cx">                     for rid in self.needs_action_rids:
</span><span class="cx">                         comp = self.calendar.overriddenComponent(rid)
</span><span class="lines">@@ -586,7 +586,7 @@
</span><span class="cx"> 
</span><span class="cx">                                 attendee.setParameter(&quot;PARTSTAT&quot;, &quot;NEEDS-ACTION&quot;)
</span><span class="cx">                 else:
</span><del>-                    log.debug(&quot;Implicit - organizer '%s' is splitting UID: '%s'&quot; % (self.organizer, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - organizer '{organizer}' is splitting UID: '{uid}'&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx"> 
</span><span class="cx">                 # Check for removed attendees
</span><span class="cx">                 if not recurrence_reschedule:
</span><span class="lines">@@ -600,10 +600,10 @@
</span><span class="cx"> 
</span><span class="cx">         elif self.action == &quot;create&quot;:
</span><span class="cx">             if self.split_details is None:
</span><del>-                log.debug(&quot;Implicit - organizer '%s' is creating UID: '%s'&quot; % (self.organizer, self.uid))
</del><ins>+                log.debug(&quot;Implicit - organizer '{organizer}' is creating UID: '{uid}'&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx">                 self.coerceAttendeesPartstatOnCreate()
</span><span class="cx">             else:
</span><del>-                log.debug(&quot;Implicit - organizer '%s' is creating a split UID: '%s'&quot; % (self.organizer, self.uid))
</del><ins>+                log.debug(&quot;Implicit - organizer '{organizer}' is creating a split UID: '{uid}'&quot;, organizer=self.organizer, uid=self.uid)
</ins><span class="cx">                 self.needs_sequence_change = False
</span><span class="cx"> 
</span><span class="cx">         # Always set RSVP=TRUE for any NEEDS-ACTION
</span><span class="lines">@@ -697,7 +697,7 @@
</span><span class="cx">                 oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx">                 newOrganizer = self.calendar.getOrganizer()
</span><span class="cx">                 if oldOrganizer != newOrganizer:
</span><del>-                    log.error(&quot;Cannot change ORGANIZER: UID:%s&quot; % (self.uid,))
</del><ins>+                    log.error(&quot;Cannot change ORGANIZER: UID:{uid}&quot;, uid=self.uid)
</ins><span class="cx">                     raise HTTPError(ErrorResponse(
</span><span class="cx">                         responsecode.FORBIDDEN,
</span><span class="cx">                         (caldav_namespace, &quot;valid-organizer-change&quot;),
</span><span class="lines">@@ -905,7 +905,7 @@
</span><span class="cx">                 if cuaddr not in coerced:
</span><span class="cx">                     attendeePrincipal = self.calendar_home.directoryService().recordWithCalendarUserAddress(cuaddr)
</span><span class="cx">                     attendeeAddress = (yield addressmapping.mapper.getCalendarUser(cuaddr, attendeePrincipal))
</span><del>-                    local_attendee = type(attendeeAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
</del><ins>+                    local_attendee = type(attendeeAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
</ins><span class="cx">                     coerced[cuaddr] = local_attendee
</span><span class="cx">                 if coerced[cuaddr]:
</span><span class="cx">                     attendee.removeParameter(&quot;SCHEDULE-AGENT&quot;)
</span><span class="lines">@@ -924,7 +924,8 @@
</span><span class="cx">         if self.action in (&quot;create&quot;, &quot;modify&quot;,):
</span><span class="cx">             total += (yield self.processRequests())
</span><span class="cx"> 
</span><del>-        self.logItems[&quot;itip.requests&quot;] = total
</del><ins>+        if self.logItems is not None:
+            self.logItems[&quot;itip.requests&quot;] = total
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -975,7 +976,7 @@
</span><span class="cx">                 scheduler = self.makeScheduler()
</span><span class="cx"> 
</span><span class="cx">                 # Do the PUT processing
</span><del>-                log.info(&quot;Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'&quot; % (self.organizer, attendee, self.uid, rids))
</del><ins>+                log.info(&quot;Implicit CANCEL - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}', RIDs: '{rids}'&quot;, organizer=self.organizer, attendee=attendee, uid=self.uid, rids=rids)
</ins><span class="cx">                 response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
</span><span class="cx">                 self.handleSchedulingResponse(response, True)
</span><span class="cx"> 
</span><span class="lines">@@ -1032,7 +1033,7 @@
</span><span class="cx">                 scheduler = self.makeScheduler()
</span><span class="cx"> 
</span><span class="cx">                 # Do the PUT processing
</span><del>-                log.info(&quot;Implicit REQUEST - organizer: '%s' to attendee: '%s', UID: '%s'&quot; % (self.organizer, attendee, self.uid,))
</del><ins>+                log.info(&quot;Implicit REQUEST - organizer: '{organizer}' to attendee: '{attendee}', UID: '{uid}'&quot;, organizer=self.organizer, attendee=attendee, uid=self.uid)
</ins><span class="cx">                 response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, internal_request=True, suppress_refresh=self.suppress_refresh))
</span><span class="cx">                 self.handleSchedulingResponse(response, True)
</span><span class="cx"> 
</span><span class="lines">@@ -1067,19 +1068,19 @@
</span><span class="cx"> 
</span><span class="cx">         if self.action == &quot;remove&quot;:
</span><span class="cx">             if self.calendar.hasPropertyValueInAllComponents(Property(&quot;STATUS&quot;, &quot;CANCELLED&quot;)):
</span><del>-                log.debug(&quot;Implicit - attendee '%s' is removing cancelled UID: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                log.debug(&quot;Implicit - attendee '{attendee}' is removing cancelled UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                 # Nothing else to do
</span><span class="cx">             elif doScheduling:
</span><span class="cx">                 # If attendee is already marked as declined in all components - nothing to do
</span><span class="cx">                 attendees = self.calendar.getAttendeeProperties((self.attendee,))
</span><span class="cx">                 if all([attendee.parameterValue(&quot;PARTSTAT&quot;, &quot;NEEDS-ACTION&quot;) == &quot;DECLINED&quot; for attendee in attendees]):
</span><del>-                    log.debug(&quot;Implicit - attendee '%s' is removing fully declined UID: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - attendee '{attendee}' is removing fully declined UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                     # Nothing else to do
</span><span class="cx">                 else:
</span><del>-                    log.debug(&quot;Implicit - attendee '%s' is cancelling UID: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - attendee '{attendee}' is cancelling UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                     yield self.scheduleCancelWithOrganizer()
</span><span class="cx">             else:
</span><del>-                log.debug(&quot;Implicit - attendee '%s' is removing UID without server scheduling: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                log.debug(&quot;Implicit - attendee '{attendee}' is removing UID without server scheduling: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                 # Nothing else to do
</span><span class="cx">             returnValue(None)
</span><span class="cx"> 
</span><span class="lines">@@ -1090,7 +1091,7 @@
</span><span class="cx">                 oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx">                 newOrganizer = self.calendar.getOrganizer()
</span><span class="cx">                 if oldOrganizer != newOrganizer:
</span><del>-                    log.error(&quot;Cannot change ORGANIZER: UID:%s&quot; % (self.uid,))
</del><ins>+                    log.error(&quot;Cannot change ORGANIZER: UID:{uid}&quot;, uid=self.uid)
</ins><span class="cx">                     raise HTTPError(ErrorResponse(
</span><span class="cx">                         responsecode.FORBIDDEN,
</span><span class="cx">                         (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1105,7 +1106,7 @@
</span><span class="cx"> 
</span><span class="cx">                 # If Organizer copy exists we cannot allow SCHEDULE-AGENT=CLIENT or NONE
</span><span class="cx">                 if not doScheduling:
</span><del>-                    log.error(&quot;Attendee '%s' is not allowed to change SCHEDULE-AGENT on organizer: UID:%s&quot; % (self.attendeePrincipal, self.uid,))
</del><ins>+                    log.error(&quot;Attendee '{attendee}' is not allowed to change SCHEDULE-AGENT on organizer: UID:{uid}&quot;, attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx">                     raise HTTPError(ErrorResponse(
</span><span class="cx">                         responsecode.FORBIDDEN,
</span><span class="cx">                         (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1119,11 +1120,11 @@
</span><span class="cx"> 
</span><span class="cx">                 if not changeAllowed:
</span><span class="cx">                     if self.calendar.hasPropertyValueInAllComponents(Property(&quot;STATUS&quot;, &quot;CANCELLED&quot;)):
</span><del>-                        log.debug(&quot;Attendee '%s' is creating CANCELLED event for mismatched UID: '%s' - removing entire event&quot; % (self.attendee, self.uid,))
</del><ins>+                        log.debug(&quot;Attendee '{attendee}' is creating CANCELLED event for mismatched UID: '{uid}' - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                         self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx">                         returnValue(None)
</span><span class="cx">                     else:
</span><del>-                        log.error(&quot;Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s&quot; % (self.attendeePrincipal, self.uid,))
</del><ins>+                        log.error(&quot;Attendee '{attendee}' is not allowed to make an unauthorized change to an organized event: UID:{uid}&quot;, attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx">                         raise HTTPError(ErrorResponse(
</span><span class="cx">                             responsecode.FORBIDDEN,
</span><span class="cx">                             (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1133,21 +1134,21 @@
</span><span class="cx">                 # Check that the return calendar actually has any components left - this can happen if a cancelled
</span><span class="cx">                 # component is removed and replaced by another cancelled or invalid one
</span><span class="cx">                 if self.calendar.mainType() is None:
</span><del>-                    log.debug(&quot;Attendee '%s' is replacing CANCELLED event: '%s' - removing entire event&quot; % (self.attendee, self.uid,))
</del><ins>+                    log.debug(&quot;Attendee '{attendee}' is replacing CANCELLED event: '{uid}' - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                     self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx">                     returnValue(None)
</span><span class="cx"> 
</span><span class="cx">                 if not doITipReply:
</span><del>-                    log.debug(&quot;Implicit - attendee '%s' is updating UID: '%s' but change is not significant&quot; % (self.attendee, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - attendee '{attendee}' is updating UID: '{uid}' but change is not significant&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                     returnValue(self.return_calendar)
</span><del>-                log.debug(&quot;Attendee '%s' is allowed to update UID: '%s' with local organizer '%s'&quot; % (self.attendee, self.uid, self.organizer))
</del><ins>+                log.debug(&quot;Attendee '{attendee}' is allowed to update UID: '{uid}' with local organizer '{organizer}'&quot;, attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx"> 
</span><span class="cx">             elif isinstance(self.organizerAddress, LocalCalendarUser):
</span><span class="cx">                 # If Organizer copy does not exists we cannot allow SCHEDULE-AGENT=SERVER
</span><span class="cx">                 if doScheduling:
</span><span class="cx">                     # Check to see whether all instances are CANCELLED
</span><span class="cx">                     if self.calendar.hasPropertyValueInAllComponents(Property(&quot;STATUS&quot;, &quot;CANCELLED&quot;)):
</span><del>-                        log.debug(&quot;Attendee '%s' is creating CANCELLED event for missing UID: '%s' - removing entire event&quot; % (self.attendee, self.uid,))
</del><ins>+                        log.debug(&quot;Attendee '{attendee}' is creating CANCELLED event for missing UID: '{uid}' - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                         self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT
</span><span class="cx">                         returnValue(None)
</span><span class="cx">                     else:
</span><span class="lines">@@ -1155,25 +1156,25 @@
</span><span class="cx">                         if self.oldcalendar:
</span><span class="cx">                             oldScheduling = self.oldcalendar.getOrganizerScheduleAgent()
</span><span class="cx">                             if not oldScheduling:
</span><del>-                                log.error(&quot;Attendee '%s' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:%s&quot; % (self.attendeePrincipal, self.uid,))
</del><ins>+                                log.error(&quot;Attendee '{attendee}' is not allowed to set SCHEDULE-AGENT=SERVER on organizer: UID:{uid}&quot;, attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx">                                 raise HTTPError(ErrorResponse(
</span><span class="cx">                                     responsecode.FORBIDDEN,
</span><span class="cx">                                     (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="cx">                                     &quot;Attendee cannot change organizer state&quot;,
</span><span class="cx">                                 ))
</span><span class="cx"> 
</span><del>-                        log.debug(&quot;Attendee '%s' is not allowed to update UID: '%s' - missing organizer copy - removing entire event&quot; % (self.attendee, self.uid,))
</del><ins>+                        log.debug(&quot;Attendee '{attendee}' is not allowed to update UID: '{uid}' - missing organizer copy - removing entire event&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                         self.return_status = ImplicitScheduler.STATUS_ORPHANED_EVENT
</span><span class="cx">                         returnValue(None)
</span><span class="cx">                 else:
</span><del>-                    log.debug(&quot;Implicit - attendee '%s' is modifying UID without server scheduling: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                    log.debug(&quot;Implicit - attendee '{attendee}' is modifying UID without server scheduling: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                     # Nothing else to do
</span><span class="cx">                     returnValue(None)
</span><span class="cx"> 
</span><span class="cx">             elif isinstance(self.organizerAddress, InvalidCalendarUser):
</span><span class="cx">                 # We will allow the attendee to do anything in this case, but we will mark the organizer
</span><span class="cx">                 # with an schedule-status error
</span><del>-                log.debug(&quot;Attendee '%s' is allowed to update UID: '%s' with invalid organizer '%s'&quot; % (self.attendee, self.uid, self.organizer))
</del><ins>+                log.debug(&quot;Attendee '{attendee}' is allowed to update UID: '{uid}' with invalid organizer '{organizer}'&quot;, attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx">                 if doScheduling:
</span><span class="cx">                     self.calendar.setParameterToValueForPropertyWithValue(
</span><span class="cx">                         &quot;SCHEDULE-STATUS&quot;,
</span><span class="lines">@@ -1187,14 +1188,14 @@
</span><span class="cx">                 # to make any change they like as we cannot verify what is reasonable. In reality
</span><span class="cx">                 # we ought to be comparing the Attendee changes against the attendee's own copy
</span><span class="cx">                 # and restrict changes based on that when the organizer's copy is not available.
</span><del>-                log.debug(&quot;Attendee '%s' is allowed to update UID: '%s' with remote organizer '%s'&quot; % (self.attendee, self.uid, self.organizer))
</del><ins>+                log.debug(&quot;Attendee '{attendee}' is allowed to update UID: '{uid}' with remote organizer '{organizer}'&quot;, attendee=self.attendee, uid=self.uid, organizer=self.organizer)
</ins><span class="cx">                 changedRids = None
</span><span class="cx"> 
</span><span class="cx">             if doScheduling:
</span><del>-                log.debug(&quot;Implicit - attendee '%s' is updating UID: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                log.debug(&quot;Implicit - attendee '{attendee}' is updating UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                 yield self.scheduleWithOrganizer(changedRids)
</span><span class="cx">             else:
</span><del>-                log.debug(&quot;Implicit - attendee '%s' is updating UID without server scheduling: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                log.debug(&quot;Implicit - attendee '{attendee}' is updating UID without server scheduling: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                 # Nothing else to do
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1203,7 +1204,7 @@
</span><span class="cx"> 
</span><span class="cx">         if self.action == &quot;remove&quot;:
</span><span class="cx">             # Nothing else to do
</span><del>-            log.debug(&quot;Implicit - missing attendee is removing UID without server scheduling: '%s'&quot; % (self.uid,))
</del><ins>+            log.debug(&quot;Implicit - missing attendee is removing UID without server scheduling: '{uid}'&quot;, uid=self.uid)
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             # Make sure ORGANIZER is not changed if originally SCHEDULE-AGENT=SERVER
</span><span class="lines">@@ -1212,7 +1213,7 @@
</span><span class="cx">                 oldOrganizer = self.oldcalendar.getOrganizer()
</span><span class="cx">                 newOrganizer = self.calendar.getOrganizer()
</span><span class="cx">                 if oldOrganizer != newOrganizer and self.oldcalendar.getOrganizerScheduleAgent():
</span><del>-                    log.error(&quot;Cannot change ORGANIZER: UID:%s&quot; % (self.uid,))
</del><ins>+                    log.error(&quot;Cannot change ORGANIZER: UID:{uid}&quot;, uid=self.uid)
</ins><span class="cx">                     raise HTTPError(ErrorResponse(
</span><span class="cx">                         responsecode.FORBIDDEN,
</span><span class="cx">                         (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1221,7 +1222,7 @@
</span><span class="cx"> 
</span><span class="cx">             # Never allow a missing attendee with a locally hosted organizer
</span><span class="cx">             if isinstance(self.organizerAddress, LocalCalendarUser):
</span><del>-                log.error(&quot;Cannot remove ATTENDEE: UID:%s&quot; % (self.uid,))
</del><ins>+                log.error(&quot;Cannot remove ATTENDEE: UID:{uid}&quot;, uid=self.uid)
</ins><span class="cx">                 raise HTTPError(ErrorResponse(
</span><span class="cx">                     responsecode.FORBIDDEN,
</span><span class="cx">                     (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1230,7 +1231,7 @@
</span><span class="cx"> 
</span><span class="cx">             # We will allow the attendee to do anything in this case, but we will mark the organizer
</span><span class="cx">             # with an schedule-status error and schedule-agent none
</span><del>-            log.debug(&quot;Missing attendee is allowed to update UID: '%s' with invalid organizer '%s'&quot; % (self.uid, self.organizer))
</del><ins>+            log.debug(&quot;Missing attendee is allowed to update UID: '{uid}' with invalid organizer '{organizer}'&quot;, uid=self.uid, organizer=self.organizer)
</ins><span class="cx"> 
</span><span class="cx">             # Check SCHEDULE-AGENT and coerce SERVER to NONE
</span><span class="cx">             if self.calendar.getOrganizerScheduleAgent():
</span><span class="lines">@@ -1241,14 +1242,14 @@
</span><span class="cx">     def checkOrganizerScheduleAgent(self):
</span><span class="cx"> 
</span><span class="cx">         is_server = self.calendar.getOrganizerScheduleAgent()
</span><del>-        local_organizer = type(self.organizerAddress) in (LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,)
</del><ins>+        local_organizer = type(self.organizerAddress) in (LocalCalendarUser, OtherServerCalendarUser,)
</ins><span class="cx"> 
</span><span class="cx">         if config.Scheduling.iMIP.Enabled and self.organizerAddress.cuaddr.lower().startswith(&quot;mailto:&quot;):
</span><span class="cx">             return is_server
</span><span class="cx"> 
</span><span class="cx">         if not config.Scheduling.iSchedule.Enabled and not local_organizer and is_server:
</span><span class="cx">             # Coerce ORGANIZER to SCHEDULE-AGENT=NONE
</span><del>-            log.debug(&quot;Attendee '%s' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:%s&quot; % (self.attendeePrincipal, self.uid,))
</del><ins>+            log.debug(&quot;Attendee '{attendee}' is not allowed to use SCHEDULE-AGENT=SERVER on organizer: UID:{uid}&quot;, attendee=self.attendeePrincipal, uid=self.uid)
</ins><span class="cx">             self.calendar.setParameterToValueForPropertyWithValue(&quot;SCHEDULE-AGENT&quot;, &quot;NONE&quot;, &quot;ORGANIZER&quot;, None)
</span><span class="cx">             self.calendar.setParameterToValueForPropertyWithValue(&quot;SCHEDULE-STATUS&quot;, iTIPRequestStatus.NO_USER_SUPPORT_CODE, &quot;ORGANIZER&quot;, None)
</span><span class="cx">             is_server = False
</span><span class="lines">@@ -1270,8 +1271,8 @@
</span><span class="cx">         calendar_resource = (yield getCalendarObjectForRecord(self.calendar_home.transaction(), self.organizerPrincipal, self.uid))
</span><span class="cx">         if calendar_resource is not None:
</span><span class="cx">             self.organizer_calendar = (yield calendar_resource.componentForUser())
</span><del>-        elif type(self.organizerAddress) in (PartitionedCalendarUser, OtherServerCalendarUser,):
-            # For partitioning where the organizer is on a different node, we will assume that the attendee's copy
</del><ins>+        elif type(self.organizerAddress) in (OtherServerCalendarUser,):
+            # For podding where the organizer is on a different node, we will assume that the attendee's copy
</ins><span class="cx">             # of the event is up to date and &quot;authoritative&quot;. So we pretend that is the organizer copy
</span><span class="cx">             self.organizer_calendar = self.oldcalendar
</span><span class="cx"> 
</span><span class="lines">@@ -1288,7 +1289,7 @@
</span><span class="cx">             oldcalendar = self.organizer_calendar
</span><span class="cx">             oldcalendar.attendeesView((self.attendee,), onlyScheduleAgentServer=True)
</span><span class="cx">             if oldcalendar.mainType() is None:
</span><del>-                log.debug(&quot;Implicit - attendee '%s' cannot use an event they are not an attendee of, UID: '%s'&quot; % (self.attendee, self.uid))
</del><ins>+                log.debug(&quot;Implicit - attendee '{attendee}' cannot use an event they are not an attendee of, UID: '{uid}'&quot;, attendee=self.attendee, uid=self.uid)
</ins><span class="cx">                 raise HTTPError(ErrorResponse(
</span><span class="cx">                     responsecode.FORBIDDEN,
</span><span class="cx">                     (caldav_namespace, &quot;valid-attendee-change&quot;),
</span><span class="lines">@@ -1303,7 +1304,8 @@
</span><span class="cx">         # First make sure we are allowed to schedule
</span><span class="cx">         self.testSchedulingAllowed()
</span><span class="cx"> 
</span><del>-        self.logItems[&quot;itip.reply&quot;] = &quot;reply&quot;
</del><ins>+        if self.logItems is not None:
+            self.logItems[&quot;itip.reply&quot;] = &quot;reply&quot;
</ins><span class="cx"> 
</span><span class="cx">         itipmsg = iTipGenerator.generateAttendeeReply(self.calendar, self.attendee, changedRids=changedRids)
</span><span class="cx"> 
</span><span class="lines">@@ -1316,7 +1318,8 @@
</span><span class="cx">         # First make sure we are allowed to schedule
</span><span class="cx">         self.testSchedulingAllowed()
</span><span class="cx"> 
</span><del>-        self.logItems[&quot;itip.reply&quot;] = &quot;cancel&quot;
</del><ins>+        if self.logItems is not None:
+            self.logItems[&quot;itip.reply&quot;] = &quot;cancel&quot;
</ins><span class="cx"> 
</span><span class="cx">         itipmsg = iTipGenerator.generateAttendeeReply(self.calendar, self.attendee, force_decline=True)
</span><span class="cx"> 
</span><span class="lines">@@ -1335,7 +1338,7 @@
</span><span class="cx">         def _gotResponse(response):
</span><span class="cx">             self.handleSchedulingResponse(response, False)
</span><span class="cx"> 
</span><del>-        log.info(&quot;Implicit %s - attendee: '%s' to organizer: '%s', UID: '%s'&quot; % (action, self.attendee, self.organizer, self.uid,))
</del><ins>+        log.info(&quot;Implicit {action} - attendee: '{attendee}' to organizer: '{organizer}', UID: '{uid}'&quot;, action=action, attendee=self.attendee, organizer=self.organizer, uid=self.uid)
</ins><span class="cx">         d = scheduler.doSchedulingViaPUT(self.originator, (self.organizer,), itipmsg, internal_request=True)
</span><span class="cx">         d.addCallback(_gotResponse)
</span><span class="cx">         return d
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduledeliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/delivery.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/delivery.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -40,8 +40,7 @@
</span><span class="cx"> from twistedcaldav.ical import normalizeCUAddress, Component
</span><span class="cx"> from twistedcaldav.util import utf8String
</span><span class="cx"> 
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser, RemoteCalendarUser, \
-    OtherServerCalendarUser
</del><ins>+from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser, OtherServerCalendarUser
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.delivery import DeliveryService
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.dkim import DKIMRequest, DKIMUtils
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.remoteservers import IScheduleServerRecord
</span><span class="lines">@@ -58,7 +57,7 @@
</span><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Handles the sending of iSchedule scheduling messages. Used for both cross-domain scheduling,
</span><del>-as well as internal partitioning or podding.
</del><ins>+as well as internal podding.
</ins><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -72,6 +71,7 @@
</span><span class="cx"> class ScheduleViaISchedule(DeliveryService):
</span><span class="cx"> 
</span><span class="cx">     domainServerMap = {}
</span><ins>+    servermgr = None
</ins><span class="cx"> 
</span><span class="cx">     @classmethod
</span><span class="cx">     def serviceType(cls):
</span><span class="lines">@@ -82,9 +82,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def matchCalendarUserAddress(cls, cuaddr):
</span><span class="cx"> 
</span><del>-        # TODO: here is where we would attempt service discovery based on the cuaddr.
-
-        # Only handle mailtos:
</del><ins>+        # Handle mailtos:
</ins><span class="cx">         if cuaddr.lower().startswith(&quot;mailto:&quot;):
</span><span class="cx">             domain = extractEmailDomain(cuaddr)
</span><span class="cx">             server = (yield cls.serverForDomain(domain))
</span><span class="lines">@@ -100,25 +98,30 @@
</span><span class="cx">     def serverForDomain(cls, domain):
</span><span class="cx">         if domain not in cls.domainServerMap:
</span><span class="cx"> 
</span><del>-            # First check built-in list of remote servers
-            servermgr = IScheduleServers()
-            server = servermgr.mapDomain(domain)
-            if server is not None:
-                cls.domainServerMap[domain] = server
-            else:
-                # Lookup domain
-                result = (yield lookupServerViaSRV(domain))
-                if result is None:
</del><ins>+            if config.Scheduling.iSchedule.Enabled:
+
+                # First check built-in list of remote servers
+                if cls.servermgr is None:
+                    cls.servermgr = IScheduleServers()
+                server = cls.servermgr.mapDomain(domain)
+                if server is not None:
+                    cls.domainServerMap[domain] = server
+                else:
</ins><span class="cx">                     # Lookup domain
</span><del>-                    result = (yield lookupServerViaSRV(domain, service=&quot;_ischedule&quot;))
</del><ins>+                    result = (yield lookupServerViaSRV(domain))
</ins><span class="cx">                     if result is None:
</span><del>-                        cls.domainServerMap[domain] = None
</del><ins>+                        # Lookup domain
+                        result = (yield lookupServerViaSRV(domain, service=&quot;_ischedule&quot;))
+                        if result is None:
+                            cls.domainServerMap[domain] = None
+                        else:
+                            # Create the iSchedule server record for this server
+                            cls.domainServerMap[domain] = IScheduleServerRecord(uri=&quot;http://%s:%s/.well-known/ischedule&quot; % result)
</ins><span class="cx">                     else:
</span><span class="cx">                         # Create the iSchedule server record for this server
</span><del>-                        cls.domainServerMap[domain] = IScheduleServerRecord(uri=&quot;http://%s:%s/.well-known/ischedule&quot; % result)
-                else:
-                    # Create the iSchedule server record for this server
-                    cls.domainServerMap[domain] = IScheduleServerRecord(uri=&quot;https://%s:%s/.well-known/ischedule&quot; % result)
</del><ins>+                        cls.domainServerMap[domain] = IScheduleServerRecord(uri=&quot;https://%s:%s/.well-known/ischedule&quot; % result)
+            else:
+                cls.domainServerMap[domain] = None
</ins><span class="cx"> 
</span><span class="cx">         returnValue(cls.domainServerMap[domain])
</span><span class="cx"> 
</span><span class="lines">@@ -136,8 +139,6 @@
</span><span class="cx">             if isinstance(recipient, RemoteCalendarUser):
</span><span class="cx">                 # Map the recipient's domain to a server
</span><span class="cx">                 server = (yield self.serverForDomain(recipient.domain))
</span><del>-            elif isinstance(recipient, PartitionedCalendarUser):
-                server = self._getServerForPartitionedUser(recipient)
</del><span class="cx">             elif isinstance(recipient, OtherServerCalendarUser):
</span><span class="cx">                 server = self._getServerForOtherServerUser(recipient)
</span><span class="cx">             else:
</span><span class="lines">@@ -182,20 +183,6 @@
</span><span class="cx">         yield DeferredList(deferreds)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def _getServerForPartitionedUser(self, recipient):
-
-        if not hasattr(self, &quot;partitionedServers&quot;):
-            self.partitionedServers = {}
-
-        partition = recipient.principal.partitionURI()
-        if partition not in self.partitionedServers:
-            self.partitionedServers[partition] = IScheduleServerRecord(uri=joinURL(partition, &quot;/ischedule&quot;))
-            self.partitionedServers[partition].unNormalizeAddresses = False
-            self.partitionedServers[partition].moreHeaders.append(recipient.principal.server().secretHeader())
-
-        return self.partitionedServers[partition]
-
-
</del><span class="cx">     def _getServerForOtherServerUser(self, recipient):
</span><span class="cx"> 
</span><span class="cx">         if not hasattr(self, &quot;otherServers&quot;):
</span><span class="lines">@@ -203,9 +190,12 @@
</span><span class="cx"> 
</span><span class="cx">         serverURI = recipient.principal.serverURI()
</span><span class="cx">         if serverURI not in self.otherServers:
</span><del>-            self.otherServers[serverURI] = IScheduleServerRecord(uri=joinURL(serverURI, &quot;/ischedule&quot;))
-            self.otherServers[serverURI].unNormalizeAddresses = not recipient.principal.server().isImplicit
-            self.otherServers[serverURI].moreHeaders.append(recipient.principal.server().secretHeader())
</del><ins>+            self.otherServers[serverURI] = IScheduleServerRecord(
+                uri=joinURL(serverURI, config.Servers.InboxName),
+                unNormalizeAddresses=not recipient.principal.server().isImplicit,
+                moreHeaders=[recipient.principal.server().secretHeader(), ],
+                podding=True,
+            )
</ins><span class="cx"> 
</span><span class="cx">         return self.otherServers[serverURI]
</span><span class="cx"> 
</span><span class="lines">@@ -222,6 +212,7 @@
</span><span class="cx">         self.refreshOnly = refreshOnly
</span><span class="cx">         self.headers = None
</span><span class="cx">         self.data = None
</span><ins>+        self.original_organizer = None
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -365,7 +356,8 @@
</span><span class="cx"> 
</span><span class="cx">         # The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
</span><span class="cx">         originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
</span><del>-        originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</del><ins>+        if self.server.unNormalizeAddresses:
+            originator = normalizeCUAddress(originator, normalizationLookup, self.scheduler.txn.directoryService().recordWithCalendarUserAddress, toUUID=False)
</ins><span class="cx">         self.headers.addRawHeader(&quot;Originator&quot;, utf8String(originator))
</span><span class="cx">         self.sign_headers.append(&quot;Originator&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -414,15 +406,15 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         if self.data is None:
</span><ins>+
</ins><span class="cx">             # Need to remap cuaddrs from urn:uuid
</span><del>-            if self.server.unNormalizeAddresses and self.scheduler.method == &quot;PUT&quot;:
-                normalizedCalendar = self.scheduler.calendar.duplicate()
</del><ins>+            normalizedCalendar = self.scheduler.calendar.duplicate()
+            self.original_organizer = normalizedCalendar.getOrganizer()
+            if self.server.unNormalizeAddresses:
</ins><span class="cx">                 normalizedCalendar.normalizeCalendarUserAddresses(
</span><span class="cx">                     normalizationLookup,
</span><span class="cx">                     self.scheduler.txn.directoryService().recordWithCalendarUserAddress,
</span><span class="cx">                     toUUID=False)
</span><del>-            else:
-                normalizedCalendar = self.scheduler.calendar
</del><span class="cx"> 
</span><span class="cx">             # For VFREEBUSY we need to strip out ATTENDEEs that do not match the recipient list
</span><span class="cx">             if self.scheduler.isfreebusy:
</span><span class="lines">@@ -445,13 +437,12 @@
</span><span class="cx">         f = Factory()
</span><span class="cx">         f.protocol = HTTPClientProtocol
</span><span class="cx">         if ssl:
</span><del>-            ep = GAIEndpoint(reactor, host, port,
-                             _configuredClientContextFactory())
</del><ins>+            ep = GAIEndpoint(reactor, host, port, _configuredClientContextFactory())
</ins><span class="cx">         else:
</span><span class="cx">             ep = GAIEndpoint(reactor, host, port)
</span><span class="cx">         proto = (yield ep.connect(f))
</span><span class="cx"> 
</span><del>-        if config.Scheduling.iSchedule.DKIM.Enabled:
</del><ins>+        if not self.server.podding() and config.Scheduling.iSchedule.DKIM.Enabled:
</ins><span class="cx">             domain, selector, key_file, algorithm, useDNSKey, useHTTPKey, usePrivateExchangeKey, expire = DKIMUtils.getConfiguration(config)
</span><span class="cx">             request = DKIMRequest(
</span><span class="cx">                 &quot;POST&quot;,
</span><span class="lines">@@ -503,6 +494,14 @@
</span><span class="cx">             calendar_data = response.childOfType(CalendarData)
</span><span class="cx">             if calendar_data:
</span><span class="cx">                 calendar_data = str(calendar_data)
</span><ins>+                if self.server.unNormalizeAddresses and self.original_organizer is not None:
+                    # Need to restore original ORGANIZER value if it got unnormalized
+                    calendar = Component.fromString(calendar_data)
+                    organizers = calendar.getAllPropertiesInAnyComponent(&quot;ORGANIZER&quot;, depth=1)
+                    for organizer in organizers:
+                        organizer.setValue(self.original_organizer)
+                    calendar_data = str(calendar)
+
</ins><span class="cx">             error = response.childOfType(Error)
</span><span class="cx">             if error:
</span><span class="cx">                 error = error.children
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischedulelocalserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/localservers.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/localservers.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -31,18 +31,12 @@
</span><span class="cx"> the principals across the whole domain need to be able to directly schedule each other and know of each others
</span><span class="cx"> existence. A common scenario would be a production server and a development/test server.
</span><span class="cx"> 
</span><del>-Each server is identified by an id and url. The id is used when assigning principals to a specific server. Each
-server can also support multiple partitions, and each of those is identified by an id and url, with the id also
-being used to assign principals to a specific partition.
</del><ins>+Each server is identified by an id and url. The id is used when assigning principals to a specific server.
</ins><span class="cx"> 
</span><del>-These servers support the concept of &quot;partitioning&quot; and &quot;podding&quot;.
</del><ins>+These servers support the concept of &quot;podding&quot;.
</ins><span class="cx"> 
</span><del>-A &quot;partitioned&quot; service is one that spreads its
-users out across multiple stores and does reverse proxying of incoming requests to the appropriate partitioned host.
-All servers within the same partition have to be running the same version of the software etc.
-
</del><span class="cx"> A &quot;podded&quot; service is one where different groups of users are hosted on different servers, which may be of
</span><del>-different versions etc. A &quot;pod&quot; may itself be &quot;partitioned&quot;, but the partitioning is &quot;invisible&quot; to the outside world.
</del><ins>+different versions etc.
</ins><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -104,13 +98,31 @@
</span><span class="cx">     def getThisServer(self):
</span><span class="cx">         return self._thisServer
</span><span class="cx"> 
</span><ins>+
+    def installReverseProxies(self, maxClients):
+        &quot;&quot;&quot;
+        Install a reverse proxy for each of the other servers in the &quot;pod&quot;.
+
+        @param maxClients: maximum number of clients in the pool.
+        @type maxClients: C{int}
+        &quot;&quot;&quot;
+
+        for server in self._servers.values():
+            if server.thisServer:
+                continue
+            installPool(
+                server.id,
+                server.uri,
+                maxClients,
+            )
+
</ins><span class="cx"> Servers = ServersDB()   # Global server DB
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class Server(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Represents a server which may itself be partitioned.
</del><ins>+    Represents a server.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def __init__(self):
</span><span class="lines">@@ -120,8 +132,6 @@
</span><span class="cx">         self.ips = set()
</span><span class="cx">         self.allowed_from_ips = set()
</span><span class="cx">         self.shared_secret = None
</span><del>-        self.partitions = {}
-        self.partitions_ips = set()
</del><span class="cx">         self.isImplicit = True
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -164,25 +174,12 @@
</span><span class="cx">                 actual_ips.add(item)
</span><span class="cx">         self.allowed_from_ips = actual_ips
</span><span class="cx"> 
</span><del>-        for uri in self.partitions.values():
-            parsed_uri = urlparse.urlparse(uri)
-            try:
-                ips = getIPsFromHost(parsed_uri.hostname)
-            except socket.gaierror, e:
-                msg = &quot;Unable to lookup ip-addr for partition '%s': %s&quot; % (parsed_uri.hostname, str(e))
-                log.error(msg)
-                if ignoreIPLookupFailures:
-                    ips = ()
-                else:
-                    raise ValueError(msg)
-            self.partitions_ips.update(ips)
</del><span class="cx"> 
</span><del>-
</del><span class="cx">     def checkThisIP(self, ip):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Check that the passed in IP address corresponds to this server or one of its partitions.
</del><ins>+        Check that the passed in IP address corresponds to this server.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        return (ip in self.ips) or (ip in self.partitions_ips)
</del><ins>+        return (ip in self.ips)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def hasAllowedFromIP(self):
</span><span class="lines">@@ -218,38 +215,13 @@
</span><span class="cx">         return (SERVER_SECRET_HEADER, self.shared_secret,)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def addPartition(self, id, uri):
-        self.partitions[id] = uri
</del><span class="cx"> 
</span><del>-
-    def getPartitionURIForId(self, id):
-        return self.partitions.get(id)
-
-
-    def isPartitioned(self):
-        return len(self.partitions) != 0
-
-
-    def installReverseProxies(self, ownUID, maxClients):
-
-        for partition, url in self.partitions.iteritems():
-            if partition != ownUID:
-                installPool(
-                    partition,
-                    url,
-                    maxClients,
-                )
-
-
-
</del><span class="cx"> ELEMENT_SERVERS = &quot;servers&quot;
</span><span class="cx"> ELEMENT_SERVER = &quot;server&quot;
</span><span class="cx"> ELEMENT_ID = &quot;id&quot;
</span><span class="cx"> ELEMENT_URI = &quot;uri&quot;
</span><span class="cx"> ELEMENT_ALLOWED_FROM = &quot;allowed-from&quot;
</span><span class="cx"> ELEMENT_SHARED_SECRET = &quot;shared-secret&quot;
</span><del>-ELEMENT_PARTITIONS = &quot;partitions&quot;
-ELEMENT_PARTITION = &quot;partition&quot;
</del><span class="cx"> ATTR_IMPLICIT = &quot;implicit&quot;
</span><span class="cx"> ATTR_VALUE_YES = &quot;yes&quot;
</span><span class="cx"> ATTR_VALUE_NO = &quot;no&quot;
</span><span class="lines">@@ -286,39 +258,13 @@
</span><span class="cx">                     server.allowed_from_ips.add(node.text)
</span><span class="cx">                 elif node.tag == ELEMENT_SHARED_SECRET:
</span><span class="cx">                     server.shared_secret = node.text
</span><del>-                elif node.tag == ELEMENT_PARTITIONS:
-                    ServersParser._parsePartition(xmlFile, node, server)
</del><span class="cx">                 else:
</span><span class="cx">                     raise RuntimeError(&quot;Invalid element '%s' in servers file: '%s'&quot; % (node.tag, xmlFile,))
</span><span class="cx"> 
</span><span class="cx">             if server.id is None or server.uri is None:
</span><del>-                raise RuntimeError(&quot;Invalid partition '%s' in servers file: '%s'&quot; % (child.tag, xmlFile,))
</del><ins>+                raise RuntimeError(&quot;Invalid server '%s' in servers file: '%s'&quot; % (child.tag, xmlFile,))
</ins><span class="cx"> 
</span><span class="cx">             server.check(ignoreIPLookupFailures=ignoreIPLookupFailures)
</span><span class="cx">             results[server.id] = server
</span><span class="cx"> 
</span><span class="cx">         return results
</span><del>-
-
-    @staticmethod
-    def _parsePartition(xmlFile, partitions, server):
-
-        for child in partitions:
-
-            if child.tag != ELEMENT_PARTITION:
-                raise RuntimeError(&quot;Unknown partition type: '%s' in servers file: '%s'&quot; % (child.tag, xmlFile,))
-
-            id = None
-            uri = None
-            for node in child:
-                if node.tag == ELEMENT_ID:
-                    id = node.text
-                elif node.tag == ELEMENT_URI:
-                    uri = node.text
-                else:
-                    raise RuntimeError(&quot;Invalid element '%s' in augment file: '%s'&quot; % (node.tag, xmlFile,))
-
-            if id is None or uri is None:
-                raise RuntimeError(&quot;Invalid partition '%s' in servers file: '%s'&quot; % (child.tag, xmlFile,))
-
-            server.addPartition(id, uri)
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleremoteserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/remoteservers.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> XML based iSchedule configuration file handling. This is for handling of remote servers. The localservers.py module
</span><del>-handles servers that are local (partitioned or podded).
</del><ins>+handles servers that are local (podded).
</ins><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> __all__ = [
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Contains server-to-server details.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    def __init__(self, uri=None):
</del><ins>+    def __init__(self, uri=None, unNormalizeAddresses=True, moreHeaders=[], podding=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param recordType: record type for directory entry.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -148,8 +148,9 @@
</span><span class="cx">         self.allow_to = True
</span><span class="cx">         self.domains = []
</span><span class="cx">         self.client_hosts = []
</span><del>-        self.unNormalizeAddresses = True
-        self.moreHeaders = []
</del><ins>+        self.unNormalizeAddresses = unNormalizeAddresses
+        self.moreHeaders = moreHeaders
+        self._podding = podding
</ins><span class="cx"> 
</span><span class="cx">         if uri:
</span><span class="cx">             self.uri = uri
</span><span class="lines">@@ -160,6 +161,10 @@
</span><span class="cx">         return (self.ssl, self.host, self.port, self.path,)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def podding(self):
+        return self._podding
+
+
</ins><span class="cx">     def redirect(self, location):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Permanent redirect for the lifetime of this record.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/resource.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/resource.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/resource.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx">     Extends L{DAVResource} to provide iSchedule inbox functionality.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, parent, store):
</del><ins>+    def __init__(self, parent, store, podding=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param parent: the parent resource of this one.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -62,6 +62,7 @@
</span><span class="cx"> 
</span><span class="cx">         self.parent = parent
</span><span class="cx">         self._newStore = store
</span><ins>+        self._podding = podding
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def deadProperties(self):
</span><span class="lines">@@ -109,12 +110,12 @@
</span><span class="cx">     def render(self, request):
</span><span class="cx">         output = &quot;&quot;&quot;&lt;html&gt;
</span><span class="cx"> &lt;head&gt;
</span><del>-&lt;title&gt;Server To Server Inbox Resource&lt;/title&gt;
</del><ins>+&lt;title&gt;%(rtype)s Inbox Resource&lt;/title&gt;
</ins><span class="cx"> &lt;/head&gt;
</span><span class="cx"> &lt;body&gt;
</span><del>-&lt;h1&gt;Server To Server Inbox Resource.&lt;/h1&gt;
</del><ins>+&lt;h1&gt;%(rtype)s Inbox Resource.&lt;/h1&gt;
</ins><span class="cx"> &lt;/body
</span><del>-&lt;/html&gt;&quot;&quot;&quot;
</del><ins>+&lt;/html&gt;&quot;&quot;&quot; % {&quot;rtype&quot; : &quot;Podding&quot; if self._podding else &quot;iSchedule&quot;, }
</ins><span class="cx"> 
</span><span class="cx">         response = Response(200, {}, output)
</span><span class="cx">         response.headers.setHeader(&quot;content-type&quot;, MimeType(&quot;text&quot;, &quot;html&quot;))
</span><span class="lines">@@ -126,7 +127,7 @@
</span><span class="cx">         The iSchedule GET method.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        if not request.args:
</del><ins>+        if not request.args or self._podding:
</ins><span class="cx">             # Do normal GET behavior
</span><span class="cx">             return self.render(request)
</span><span class="cx"> 
</span><span class="lines">@@ -229,7 +230,7 @@
</span><span class="cx">         txn = transactionFromRequest(request, self._newStore)
</span><span class="cx"> 
</span><span class="cx">         # This is a server-to-server scheduling operation.
</span><del>-        scheduler = IScheduleScheduler(txn, None)
</del><ins>+        scheduler = IScheduleScheduler(txn, None, podding=self._podding)
</ins><span class="cx"> 
</span><span class="cx">         # Check content first
</span><span class="cx">         contentType = request.headers.getHeader(&quot;content-type&quot;)
</span><span class="lines">@@ -259,7 +260,8 @@
</span><span class="cx">         else:
</span><span class="cx">             yield txn.commit()
</span><span class="cx">         response = result.response(format=format)
</span><del>-        response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
</del><ins>+        if not self._podding:
+            response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
</ins><span class="cx">         returnValue(response)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduleschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/scheduler.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/scheduler.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -119,6 +119,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class IScheduleScheduler(RemoteScheduler):
</span><ins>+    &quot;&quot;&quot;
+    Handles iSchedule and podding requests.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><span class="cx">     scheduleResponse = IScheduleResponseQueue
</span><span class="cx"> 
</span><span class="lines">@@ -138,6 +141,11 @@
</span><span class="cx">         &quot;max-recipients&quot;: (ischedule_namespace, &quot;max-recipients&quot;),
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    def __init__(self, txn, originator_uid, logItems=None, noAttendeeRefresh=False, podding=False):
+        super(IScheduleScheduler, self).__init__(txn, originator_uid, logItems=logItems, noAttendeeRefresh=noAttendeeRefresh)
+        self._podding = podding
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def doSchedulingViaPOST(self, remoteAddr, headers, body, calendar, originator, recipients):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -148,7 +156,7 @@
</span><span class="cx">         self.headers = headers
</span><span class="cx">         self.verified = False
</span><span class="cx"> 
</span><del>-        if config.Scheduling.iSchedule.DKIM.Enabled:
</del><ins>+        if not self._podding and config.Scheduling.iSchedule.DKIM.Enabled:
</ins><span class="cx">             verifier = DKIMVerifier(self.headers, body, protocol_debug=config.Scheduling.iSchedule.DKIM.ProtocolDebug)
</span><span class="cx">             try:
</span><span class="cx">                 yield verifier.verify()
</span><span class="lines">@@ -172,11 +180,16 @@
</span><span class="cx">                     msg,
</span><span class="cx">                 ))
</span><span class="cx"> 
</span><del>-        if self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', (&quot;F&quot;))[0] == &quot;T&quot;:
</del><ins>+        if self._podding and self.headers.getRawHeaders('x-calendarserver-itip-refreshonly', (&quot;F&quot;))[0] == &quot;T&quot;:
</ins><span class="cx">             self.txn.doing_attendee_refresh = 1
</span><span class="cx"> 
</span><span class="cx">         # Normalize recipient addresses
</span><del>-        recipients = [normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress) for recipient in recipients]
</del><ins>+        results = []
+        for recipient in recipients:
+            normalized = normalizeCUAddress(recipient, normalizationLookup, self.txn.directoryService().recordWithCalendarUserAddress)
+            self.recipientsNormalizationMap[normalized] = recipient
+            results.append(normalized)
+        recipients = results
</ins><span class="cx"> 
</span><span class="cx">         result = (yield super(IScheduleScheduler, self).doSchedulingViaPOST(originator, recipients, calendar))
</span><span class="cx">         returnValue(result)
</span><span class="lines">@@ -216,7 +229,7 @@
</span><span class="cx">         originatorPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.originator)
</span><span class="cx">         localUser = (yield addressmapping.mapper.isCalendarUserInMyDomain(self.originator))
</span><span class="cx">         if originatorPrincipal or localUser:
</span><del>-            if originatorPrincipal.locallyHosted():
</del><ins>+            if originatorPrincipal.thisServer():
</ins><span class="cx">                 log.error(&quot;Cannot use originator that is on this server: %s&quot; % (self.originator,))
</span><span class="cx">                 raise HTTPError(self.errorResponse(
</span><span class="cx">                     responsecode.FORBIDDEN,
</span><span class="lines">@@ -294,23 +307,17 @@
</span><span class="cx"> 
</span><span class="cx">     def _validAlternateServer(self, principal):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Check the validity of the partitioned host.
</del><ins>+        Check the validity of the podded host.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        # Extract expected host/port. This will be the partitionURI, or if no partitions,
-        # the serverURI
-        expected_uri = principal.partitionURI()
-        if expected_uri is None:
-            expected_uri = principal.serverURI()
</del><ins>+        # Extract expected host/port. This will be the serverURI.
+        expected_uri = principal.serverURI()
</ins><span class="cx">         expected_uri = urlparse.urlparse(expected_uri)
</span><span class="cx"> 
</span><span class="cx">         # Get the request IP and map to hostname.
</span><span class="cx">         clientip = self.remoteAddr.host
</span><span class="cx"> 
</span><del>-        # Check against this server (or any of its partitions). We need this because an external iTIP message
-        # may be addressed to users on different partitions, and the node receiving the iTIP message will need to
-        # forward it to the partition nodes, thus the client ip seen by the partitions will in fact be the initial
-        # receiving node.
</del><ins>+        # Check against this server.
</ins><span class="cx">         matched = False
</span><span class="cx">         if Servers.getThisServer().checkThisIP(clientip):
</span><span class="cx">             matched = True
</span><span class="lines">@@ -362,7 +369,7 @@
</span><span class="cx">         if organizer:
</span><span class="cx">             organizerPrincipal = self.txn.directoryService().recordWithCalendarUserAddress(organizer)
</span><span class="cx">             if organizerPrincipal:
</span><del>-                if organizerPrincipal.locallyHosted():
</del><ins>+                if organizerPrincipal.thisServer():
</ins><span class="cx">                     log.error(&quot;Invalid ORGANIZER in calendar data: %s&quot; % (self.calendar,))
</span><span class="cx">                     raise HTTPError(self.errorResponse(
</span><span class="cx">                         responsecode.FORBIDDEN,
</span><span class="lines">@@ -370,7 +377,7 @@
</span><span class="cx">                         &quot;Organizer is not local to server&quot;,
</span><span class="cx">                     ))
</span><span class="cx">                 else:
</span><del>-                    # Check that the origin server is the correct partition
</del><ins>+                    # Check that the origin server is the correct pod
</ins><span class="cx">                     self.organizer = calendarUserFromPrincipal(organizer, organizerPrincipal)
</span><span class="cx">                     self._validAlternateServer(self.organizer.principal)
</span><span class="cx">             else:
</span><span class="lines">@@ -403,7 +410,7 @@
</span><span class="cx">         # Attendee cannot be local.
</span><span class="cx">         attendeePrincipal = self.txn.directoryService().recordWithCalendarUserAddress(self.attendee)
</span><span class="cx">         if attendeePrincipal:
</span><del>-            if attendeePrincipal.locallyHosted():
</del><ins>+            if attendeePrincipal.thisServer():
</ins><span class="cx">                 log.error(&quot;Invalid ATTENDEE in calendar data: %s&quot; % (self.calendar,))
</span><span class="cx">                 raise HTTPError(self.errorResponse(
</span><span class="cx">                     responsecode.FORBIDDEN,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduletesttest_deliverypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_delivery.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -42,6 +42,7 @@
</span><span class="cx">         Make sure we do an exact comparison on EmailDomain
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+        self.patch(config.Scheduling.iSchedule, &quot;Enabled&quot;, True)
</ins><span class="cx">         self.patch(config.Scheduling.iSchedule, &quot;RemoteServers&quot;, &quot;&quot;)
</span><span class="cx"> 
</span><span class="cx">         # Only mailtos:
</span><span class="lines">@@ -64,3 +65,9 @@
</span><span class="cx">         self.assertFalse(result)
</span><span class="cx">         result = yield ScheduleViaISchedule.matchCalendarUserAddress(&quot;mailto:user&quot;)
</span><span class="cx">         self.assertFalse(result)
</span><ins>+
+        # Test when not enabled
+        ScheduleViaISchedule.domainServerMap = {}
+        self.patch(config.Scheduling.iSchedule, &quot;Enabled&quot;, False)
+        result = yield ScheduleViaISchedule.matchCalendarUserAddress(&quot;mailto:user@example.com&quot;)
+        self.assertFalse(result)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingischeduletesttest_localserverspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -37,16 +37,6 @@
</span><span class="cx">   &lt;server&gt;
</span><span class="cx">     &lt;id&gt;00002&lt;/id&gt;
</span><span class="cx">     &lt;uri&gt;https://caldav2.example.com:8843&lt;/uri&gt;
</span><del>-    &lt;partitions&gt;
-        &lt;partition&gt;
-            &lt;id&gt;A&lt;/id&gt;
-            &lt;uri&gt;https://machine1.example.com:8443&lt;/uri&gt;
-        &lt;/partition&gt;
-        &lt;partition&gt;
-            &lt;id&gt;B&lt;/id&gt;
-            &lt;uri&gt;https://machine2.example.com:8443&lt;/uri&gt;
-        &lt;/partition&gt;
-    &lt;/partitions&gt;
</del><span class="cx">   &lt;/server&gt;
</span><span class="cx"> &lt;/servers&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="lines">@@ -62,16 +52,6 @@
</span><span class="cx">   &lt;server&gt;
</span><span class="cx">     &lt;id&gt;00002&lt;/id&gt;
</span><span class="cx">     &lt;uri&gt;https://caldav2.example.com:8843&lt;/uri&gt;
</span><del>-    &lt;partitions&gt;
-        &lt;partition&gt;
-            &lt;id&gt;A&lt;/id&gt;
-            &lt;uri&gt;https://machine1.example.com:8443&lt;/uri&gt;
-        &lt;/partition&gt;
-        &lt;partition&gt;
-            &lt;id&gt;B&lt;/id&gt;
-            &lt;uri&gt;https://machine2.example.com:8443&lt;/uri&gt;
-        &lt;/partition&gt;
-    &lt;/partitions&gt;
</del><span class="cx">   &lt;/server&gt;
</span><span class="cx"> &lt;/servers&gt;
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="lines">@@ -103,13 +83,7 @@
</span><span class="cx">         self.assertEqual(servers.getServerById(&quot;00001&quot;).shared_secret, &quot;foobar&quot;)
</span><span class="cx">         self.assertEqual(servers.getServerById(&quot;00002&quot;).shared_secret, None)
</span><span class="cx"> 
</span><del>-        self.assertEqual(len(servers.getServerById(&quot;00001&quot;).partitions), 0)
-        self.assertEqual(len(servers.getServerById(&quot;00002&quot;).partitions), 2)
</del><span class="cx"> 
</span><del>-        self.assertEqual(servers.getServerById(&quot;00002&quot;).getPartitionURIForId(&quot;A&quot;), &quot;https://machine1.example.com:8443&quot;)
-        self.assertEqual(servers.getServerById(&quot;00002&quot;).getPartitionURIForId(&quot;B&quot;), &quot;https://machine2.example.com:8443&quot;)
-
-
</del><span class="cx">     def test_this_server(self):
</span><span class="cx"> 
</span><span class="cx">         servers = self._setupServers()
</span><span class="lines">@@ -129,14 +103,6 @@
</span><span class="cx">         self.assertTrue(servers.getServerById(&quot;00002&quot;).thisServer)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def test_check_is_partitioned(self):
-
-        servers = self._setupServers()
-
-        self.assertFalse(servers.getServerById(&quot;00001&quot;).isPartitioned())
-        self.assertTrue(servers.getServerById(&quot;00002&quot;).isPartitioned())
-
-
</del><span class="cx">     def test_check_this_ip(self):
</span><span class="cx"> 
</span><span class="cx">         servers = self._setupServers()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingitippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/itip.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/itip.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/itip.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -476,16 +476,9 @@
</span><span class="cx">                 pass
</span><span class="cx"> 
</span><span class="cx">             elif attendee_comment is None and private_comment is not None:
</span><del>-                # Remove all property parameters
-                private_comment.removeAllParameters()
</del><ins>+                # We now remove the private comment on the organizer's side if the attendee removed it
+                to_component.removeProperty(private_comment)
</ins><span class="cx"> 
</span><del>-                # Add default parameters
-                private_comment.setParameter(&quot;X-CALENDARSERVER-ATTENDEE-REF&quot;, attendee.value())
-                private_comment.setParameter(&quot;X-CALENDARSERVER-DTSTAMP&quot;, DateTime.getNowUTC().getText())
-
-                # Set value empty
-                private_comment.setValue(&quot;&quot;)
-
</del><span class="cx">                 private_comment_changed = True
</span><span class="cx"> 
</span><span class="cx">             elif attendee_comment is not None and private_comment is None:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingschedulerpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/scheduler.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/scheduler.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/scheduler.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -38,7 +38,6 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import LocalCalendarUser
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import RemoteCalendarUser
</span><span class="cx"> from txdav.caldav.datastore.scheduling.cuaddress import EmailCalendarUser
</span><del>-from txdav.caldav.datastore.scheduling.cuaddress import PartitionedCalendarUser
</del><span class="cx"> from txdav.caldav.datastore.scheduling.imip.delivery import ScheduleViaIMip
</span><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule
</span><span class="cx"> from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
</span><span class="lines">@@ -52,8 +51,8 @@
</span><span class="cx"> This module handles the delivery of scheduling messages to organizer and attendees. The basic idea is to first
</span><span class="cx"> confirm the integrity of the incoming scheduling message, check authorization. Appropriate L{DeliveryService}s
</span><span class="cx"> are then used to deliver the message to attendees or organizer. Delivery responses are processed and returned.
</span><del>-This takes into account partitioning and podding of users by detecting the appropriate host for a calendar
-user and then dispatching the delivery accordingly.
</del><ins>+This takes into account podding of users by detecting the appropriate host for a calendar user and then
+dispatching the delivery accordingly.
</ins><span class="cx"> 
</span><span class="cx"> The L{Scheduler} class defines the basic behavior for processing deliveries. Sub-classes are defined for the
</span><span class="cx"> different ways a deliver can be triggered.
</span><span class="lines">@@ -144,6 +143,7 @@
</span><span class="cx"> 
</span><span class="cx">         self.originator = None
</span><span class="cx">         self.recipients = None
</span><ins>+        self.recipientsNormalizationMap = {}
</ins><span class="cx">         self.calendar = None
</span><span class="cx">         self.organizer = None
</span><span class="cx">         self.attendee = None
</span><span class="lines">@@ -234,51 +234,6 @@
</span><span class="cx">         returnValue(result)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @inlineCallbacks
-    def loadFromRequestData(self):
-        self.loadOriginatorFromRequestDetails()
-        self.loadRecipientsFromCalendarData()
-
-
-    def loadOriginatorFromRequestDetails(self):
-        # Get the originator who is the authenticated user
-        originatorPrincipal = self.txn.directoryService().recordWithUID(self.originator_uid)
-
-        # Pick the canonical CUA:
-        originator = originatorPrincipal.canonicalCalendarUserAddress() if originatorPrincipal else &quot;&quot;
-
-        if not originator:
-            log.error(&quot;%s request must have Originator&quot; % (self.method,))
-            raise HTTPError(self.errorResponse(
-                responsecode.FORBIDDEN,
-                self.errorElements[&quot;originator-missing&quot;],
-                &quot;Missing originator&quot;,
-            ))
-        else:
-            self.originator = originator
-
-
-    def loadRecipientsFromCalendarData(self):
-
-        # Get the ATTENDEEs
-        attendees = list()
-        unique_set = set()
-        for attendee, _ignore in self.calendar.getAttendeesByInstance():
-            if attendee not in unique_set:
-                attendees.append(attendee)
-                unique_set.add(attendee)
-
-        if not attendees:
-            log.error(&quot;%s request must have at least one Recipient&quot; % (self.method,))
-            raise HTTPError(self.errorResponse(
-                responsecode.FORBIDDEN,
-                self.errorElements[&quot;recipient-missing&quot;],
-                &quot;Must have recipients&quot;,
-            ))
-        else:
-            self.recipients = list(attendees)
-
-
</del><span class="cx">     def preProcessCalendarData(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         After loading calendar data from the request, do some optional processing of it. This method will be
</span><span class="lines">@@ -476,11 +431,10 @@
</span><span class="cx">         freebusy = self.checkForFreeBusy()
</span><span class="cx"> 
</span><span class="cx">         # Prepare for multiple responses
</span><del>-        responses = self.scheduleResponse(self.method, responsecode.OK)
</del><ins>+        responses = self.scheduleResponse(self.method, responsecode.OK, self.mapRecipientAddress)
</ins><span class="cx"> 
</span><span class="cx">         # Loop over each recipient and aggregate into lists by service types.
</span><span class="cx">         caldav_recipients = []
</span><del>-        partitioned_recipients = []
</del><span class="cx">         otherserver_recipients = []
</span><span class="cx">         remote_recipients = []
</span><span class="cx">         imip_recipients = []
</span><span class="lines">@@ -502,9 +456,6 @@
</span><span class="cx">             elif isinstance(recipient, LocalCalendarUser):
</span><span class="cx">                 caldav_recipients.append(recipient)
</span><span class="cx"> 
</span><del>-            elif isinstance(recipient, PartitionedCalendarUser):
-                partitioned_recipients.append(recipient)
-
</del><span class="cx">             elif isinstance(recipient, OtherServerCalendarUser):
</span><span class="cx">                 otherserver_recipients.append(recipient)
</span><span class="cx"> 
</span><span class="lines">@@ -526,10 +477,6 @@
</span><span class="cx">         if caldav_recipients:
</span><span class="cx">             yield self.generateLocalSchedulingResponses(caldav_recipients, responses, freebusy)
</span><span class="cx"> 
</span><del>-        # Now process partitioned recipients
-        if partitioned_recipients:
-            yield self.generateRemoteSchedulingResponses(partitioned_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
-
</del><span class="cx">         # Now process other server recipients
</span><span class="cx">         if otherserver_recipients:
</span><span class="cx">             yield self.generateRemoteSchedulingResponses(otherserver_recipients, responses, freebusy, getattr(self.txn, 'doing_attendee_refresh', False))
</span><span class="lines">@@ -579,7 +526,11 @@
</span><span class="cx">         return requestor.generateSchedulingResponses()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def mapRecipientAddress(self, cuaddr):
+        return self.recipientsNormalizationMap.get(cuaddr, cuaddr)
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class RemoteScheduler(Scheduler):
</span><span class="cx"> 
</span><span class="cx">     def checkOrganizer(self):
</span><span class="lines">@@ -614,8 +565,8 @@
</span><span class="cx">             else:
</span><span class="cx">                 # Map recipient to their inbox
</span><span class="cx">                 inbox = None
</span><del>-                if principal.calendarsEnabled() and principal.thisServer():
-                    if principal.locallyHosted():
</del><ins>+                if principal.calendarsEnabled():
+                    if principal.thisServer():
</ins><span class="cx">                         recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
</span><span class="cx">                         if recipient_home:
</span><span class="cx">                             inbox = (yield recipient_home.calendarWithName(&quot;inbox&quot;))
</span><span class="lines">@@ -710,7 +661,7 @@
</span><span class="cx">         [&quot;recipient&quot;, &quot;reqstatus&quot;, &quot;calendar&quot;, &quot;error&quot;, &quot;message&quot;, ]
</span><span class="cx">     )
</span><span class="cx"> 
</span><del>-    def __init__(self, method, success_response):
</del><ins>+    def __init__(self, method, success_response, recipient_mapper=None):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param method: the name of the method generating the queue.
</span><span class="cx">         @param success_response: the response to return in lieu of a
</span><span class="lines">@@ -719,6 +670,7 @@
</span><span class="cx">         self.responses = []
</span><span class="cx">         self.method = method
</span><span class="cx">         self.success_response = success_response
</span><ins>+        self.recipient_mapper = recipient_mapper
</ins><span class="cx">         self.location = None
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -752,6 +704,9 @@
</span><span class="cx">         else:
</span><span class="cx">             raise AssertionError(&quot;Unknown data type: %r&quot; % (what,))
</span><span class="cx"> 
</span><ins>+        if self.recipient_mapper is not None:
+            recipient = self.recipient_mapper(recipient)
+
</ins><span class="cx">         if not suppressErrorLog and code &gt; 400: # Error codes only
</span><span class="cx">             self.log.error(&quot;Error during %s for %s: %s&quot; % (self.method, recipient, message))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoreschedulingutilspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/utils.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/utils.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/scheduling/utils.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -21,7 +21,7 @@
</span><span class="cx"> log = Logger()
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def getCalendarObjectForRecord(txn, record, uid, allow_shared=False):
</del><ins>+def getCalendarObjectForRecord(txn, record, uid):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Get a copy of the event for a calendar user identified by a directory record.
</span><span class="cx"> 
</span><span class="lines">@@ -29,12 +29,12 @@
</span><span class="cx">     one of them to avoid scheduling problems.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    if record and record.locallyHosted():
</del><ins>+    if record and record.thisServer():
</ins><span class="cx">         # Get record's calendar-home
</span><span class="cx">         calendar_home = yield txn.calendarHomeWithUID(record.uid)
</span><span class="cx"> 
</span><span class="cx">         # Get matching newstore objects
</span><del>-        objectResources = (yield calendar_home.getCalendarResourcesForUID(uid, allow_shared))
</del><ins>+        objectResources = (yield calendar_home.getCalendarResourcesForUID(uid))
</ins><span class="cx"> 
</span><span class="cx">         if len(objectResources) &gt; 1:
</span><span class="cx">             # Delete all but the first one
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/sql.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/sql.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/sql.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -546,9 +546,7 @@
</span><span class="cx">         # refer to calendar *object* UIDs, since calendar *resources* are an
</span><span class="cx">         # HTTP protocol layer thing, not a data store thing.  (See also
</span><span class="cx">         # objectResourcesWithUID.)
</span><del>-        objectResources = (
-            yield self.objectResourcesWithUID(uid, [&quot;inbox&quot;], False)
-        )
</del><ins>+        objectResources = (yield self.getCalendarResourcesForUID(uid))
</ins><span class="cx">         for objectResource in objectResources:
</span><span class="cx">             if ok_object and objectResource._resourceID == ok_object._resourceID:
</span><span class="cx">                 continue
</span><span class="lines">@@ -560,15 +558,22 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def getCalendarResourcesForUID(self, uid, allow_shared=False):
</del><ins>+    def getCalendarResourcesForUID(self, uid):
+        &quot;&quot;&quot;
+        Find all calendar object resources in the calendar home that are not in the &quot;inbox&quot; collection
+        and not in shared collections.
+        Cache the result of this query as it can happen multiple times during scheduling under slightly
+        different circumstances.
</ins><span class="cx"> 
</span><del>-        results = []
-        objectResources = (yield self.objectResourcesWithUID(uid, [&quot;inbox&quot;]))
-        for objectResource in objectResources:
-            if allow_shared or objectResource._parentCollection.owned():
-                results.append(objectResource)
</del><ins>+        @param uid: the UID of the calendar object resources to find
+        @type uid: C{str}
+        &quot;&quot;&quot;
</ins><span class="cx"> 
</span><del>-        returnValue(results)
</del><ins>+        if not hasattr(self, &quot;_cachedCalendarResourcesForUID&quot;):
+            self._cachedCalendarResourcesForUID = {}
+        if uid not in self._cachedCalendarResourcesForUID:
+            self._cachedCalendarResourcesForUID[uid] = (yield self.objectResourcesWithUID(uid, [&quot;inbox&quot;], allowShared=False))
+        returnValue(self._cachedCalendarResourcesForUID[uid])
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1566,10 +1571,6 @@
</span><span class="cx">                 if calsize &gt; config.MaxResourceSize:
</span><span class="cx">                     raise ObjectResourceTooBigError()
</span><span class="cx"> 
</span><del>-        # Possible timezone stripping
-        if config.EnableTimezonesByReference:
-            component.stripKnownTimezones()
-
</del><span class="cx">         # Do validation on external requests
</span><span class="cx">         if internal_state == ComponentUpdateState.NORMAL:
</span><span class="cx"> 
</span><span class="lines">@@ -1587,6 +1588,10 @@
</span><span class="cx">             # calendar data
</span><span class="cx">             component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)
</span><span class="cx"> 
</span><ins>+        # Possible timezone stripping
+        if config.EnableTimezonesByReference:
+            component.stripKnownTimezones()
+
</ins><span class="cx">         # Check location/resource organizer requirement
</span><span class="cx">         self.validLocationResourceOrganizer(component, inserting, internal_state)
</span><span class="cx"> 
</span><span class="lines">@@ -1721,20 +1726,23 @@
</span><span class="cx"> 
</span><span class="cx">         NB Do this before implicit scheduling as we don't want old clients to trigger scheduling when
</span><span class="cx">         the X- property is missing.
</span><ins>+
+        We now only preserve the &quot;X-CALENDARSERVER-ATTENDEE-COMMENT&quot; property. We will now allow clients
+        to delete the &quot;X-CALENDARSERVER-PRIVATE-COMMENT&quot; and treat that as a removal of the attendee
+        comment (which will trigger scheduling with the organizer to remove the comment on the organizer's
+        side).
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if config.Scheduling.CalDAV.get(&quot;EnablePrivateComments&quot;, True):
</span><span class="cx">             old_has_private_comments = not inserting and self.hasPrivateComment
</span><span class="cx">             new_has_private_comments = component.hasPropertyInAnyComponent((
</span><del>-                &quot;X-CALENDARSERVER-PRIVATE-COMMENT&quot;,
</del><span class="cx">                 &quot;X-CALENDARSERVER-ATTENDEE-COMMENT&quot;,
</span><span class="cx">             ))
</span><span class="cx"> 
</span><span class="cx">             if old_has_private_comments and not new_has_private_comments:
</span><span class="cx">                 # Transfer old comments to new calendar
</span><del>-                log.debug(&quot;Private Comments properties were entirely removed by the client. Restoring existing properties.&quot;)
</del><ins>+                log.debug(&quot;Organizer private comment properties were entirely removed by the client. Restoring existing properties.&quot;)
</ins><span class="cx">                 old_calendar = (yield self.componentForUser())
</span><span class="cx">                 component.transferProperties(old_calendar, (
</span><del>-                    &quot;X-CALENDARSERVER-PRIVATE-COMMENT&quot;,
</del><span class="cx">                     &quot;X-CALENDARSERVER-ATTENDEE-COMMENT&quot;,
</span><span class="cx">                 ))
</span><span class="cx"> 
</span><span class="lines">@@ -1943,7 +1951,7 @@
</span><span class="cx">                 user_uuid = self._parentCollection.viewerHome().uid()
</span><span class="cx">                 component = PerUserDataFilter(user_uuid).filter(component.duplicate())
</span><span class="cx"> 
</span><del>-            scheduler = ImplicitScheduler()
</del><ins>+            scheduler = ImplicitScheduler(logItems=self._txn.logItems)
</ins><span class="cx"> 
</span><span class="cx">             # PUT
</span><span class="cx">             do_implicit_action, is_scheduling_resource = (yield scheduler.testImplicitSchedulingPUT(
</span><span class="lines">@@ -2600,7 +2608,7 @@
</span><span class="cx">         if not isinbox and internal_state == ComponentRemoveState.NORMAL:
</span><span class="cx">             # Get data we need for implicit scheduling
</span><span class="cx">             calendar = (yield self.componentForUser())
</span><del>-            scheduler = ImplicitScheduler()
</del><ins>+            scheduler = ImplicitScheduler(logItems=self._txn.logItems)
</ins><span class="cx">             do_implicit_action, _ignore = (yield scheduler.testImplicitSchedulingDELETE(
</span><span class="cx">                 self.calendar(),
</span><span class="cx">                 self,
</span><span class="lines">@@ -2919,7 +2927,7 @@
</span><span class="cx"> 
</span><span class="cx">         # Only allow organizers to manipulate managed attachments for now
</span><span class="cx">         calendar = (yield self.componentForUser())
</span><del>-        scheduler = ImplicitScheduler()
</del><ins>+        scheduler = ImplicitScheduler(logItems=self._txn.logItems)
</ins><span class="cx">         is_attendee = (yield scheduler.testAttendeeEvent(self.calendar(), self, calendar,))
</span><span class="cx">         if is_attendee:
</span><span class="cx">             raise InvalidAttachmentOperation(&quot;Attendees are not allowed to manipulate managed attachments&quot;)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/common.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/common.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/common.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -84,73 +84,75 @@
</span><span class="cx"> 
</span><span class="cx"> OTHER_HOME_UID = &quot;home_splits&quot;
</span><span class="cx"> 
</span><del>-test_event_text = (
-    &quot;BEGIN:VCALENDAR\r\n&quot;
-      &quot;VERSION:2.0\r\n&quot;
-      &quot;PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n&quot;
-      &quot;CALSCALE:GREGORIAN\r\n&quot;
-      &quot;BEGIN:VTIMEZONE\r\n&quot;
-        &quot;TZID:US/Pacific\r\n&quot;
-        &quot;BEGIN:DAYLIGHT\r\n&quot;
-          &quot;TZOFFSETFROM:-0800\r\n&quot;
-          &quot;RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n&quot;
-          &quot;DTSTART:20070311T020000\r\n&quot;
-          &quot;TZNAME:PDT\r\n&quot;
-          &quot;TZOFFSETTO:-0700\r\n&quot;
-        &quot;END:DAYLIGHT\r\n&quot;
-        &quot;BEGIN:STANDARD\r\n&quot;
-          &quot;TZOFFSETFROM:-0700\r\n&quot;
-          &quot;RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n&quot;
-          &quot;DTSTART:20071104T020000\r\n&quot;
-          &quot;TZNAME:PST\r\n&quot;
-          &quot;TZOFFSETTO:-0800\r\n&quot;
-        &quot;END:STANDARD\r\n&quot;
-      &quot;END:VTIMEZONE\r\n&quot;
-      &quot;BEGIN:VEVENT\r\n&quot;
-        &quot;CREATED:20100203T013849Z\r\n&quot;
-        &quot;UID:uid-test\r\n&quot;
-        &quot;DTEND;TZID=US/Pacific:20100207T173000\r\n&quot;
-        &quot;TRANSP:OPAQUE\r\n&quot;
-        &quot;SUMMARY:New Event\r\n&quot;
-        &quot;DTSTART;TZID=US/Pacific:20100207T170000\r\n&quot;
-        &quot;DTSTAMP:20100203T013909Z\r\n&quot;
-        &quot;SEQUENCE:3\r\n&quot;
-        &quot;X-APPLE-DROPBOX:/calendars/users/wsanchez/dropbox/uid-test.dropbox\r\n&quot;
-        &quot;BEGIN:VALARM\r\n&quot;
-          &quot;X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n&quot;
-          &quot;TRIGGER:-PT20M\r\n&quot;
-          &quot;ATTACH:Basso\r\n&quot;
-          &quot;ACTION:AUDIO\r\n&quot;
-        &quot;END:VALARM\r\n&quot;
-      &quot;END:VEVENT\r\n&quot;
-    &quot;END:VCALENDAR\r\n&quot;
-)
</del><ins>+test_event_text = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:PDT
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:PST
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20100203T013849Z
+UID:uid-test
+DTEND;TZID=US/Pacific:20100207T173000
+TRANSP:OPAQUE
+SUMMARY:New Event
+DTSTART;TZID=US/Pacific:20100207T170000
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+X-APPLE-DROPBOX:/calendars/users/wsanchez/dropbox/uid-test.dropbox
+BEGIN:VALARM
+X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
+TRIGGER:-PT20M
+ATTACH:Basso
+ACTION:AUDIO
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-test_event_notCalDAV_text = (
-    &quot;BEGIN:VCALENDAR\r\n&quot;
-      &quot;VERSION:2.0\r\n&quot;
-      &quot;PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n&quot;
-      &quot;CALSCALE:GREGORIAN\r\n&quot;
-      &quot;BEGIN:VEVENT\r\n&quot;
-        &quot;CREATED:20100203T013849Z\r\n&quot;
-        &quot;UID:test\r\n&quot;
-        &quot;DTEND;TZID=US/Pacific:20100207T173000\r\n&quot; # TZID without VTIMEZONE
-        &quot;TRANSP:OPAQUE\r\n&quot;
-        &quot;SUMMARY:New Event\r\n&quot;
-        &quot;DTSTART;TZID=US/Pacific:20100207T170000\r\n&quot;
-        &quot;DTSTAMP:20100203T013909Z\r\n&quot;
-        &quot;SEQUENCE:3\r\n&quot;
-        &quot;BEGIN:VALARM\r\n&quot;
-          &quot;X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n&quot;
-          &quot;TRIGGER:-PT20M\r\n&quot;
-          &quot;ATTACH:Basso\r\n&quot;
-          &quot;ACTION:AUDIO\r\n&quot;
-        &quot;END:VALARM\r\n&quot;
-      &quot;END:VEVENT\r\n&quot;
-    &quot;END:VCALENDAR\r\n&quot;
-)
</del><ins>+test_event_notCalDAV_text = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100203T013849Z
+UID:test-bad1
+DTEND:20100207T173000Z
+TRANSP:OPAQUE
+SUMMARY:New Event
+DTSTART:20100207T170000Z
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20100203T013849Z
+UID:test-bad2
+DTEND:20100207T173000Z
+TRANSP:OPAQUE
+SUMMARY:New Event
+DTSTART:20100207T170000Z
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -450,9 +452,7 @@
</span><span class="cx">         yield notifications.writeNotificationObject(&quot;abc&quot;, inviteNotification,
</span><span class="cx">             inviteNotification.toxml())
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notification fired after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -460,6 +460,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home1/notification/&quot;,
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         notifications = yield self.transactionUnderTest().notificationsWithUID(
</span><span class="cx">             &quot;home1&quot;
</span><span class="lines">@@ -469,9 +470,7 @@
</span><span class="cx">         abc = yield notifications.notificationObjectWithUID(&quot;abc&quot;)
</span><span class="cx">         self.assertEquals(abc, None)
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notification fired after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -479,6 +478,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home1/notification/&quot;,
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -697,11 +697,10 @@
</span><span class="cx">         self.assertNotIdentical((yield home.calendarWithName(name)), None)
</span><span class="cx">         calendarProperties = (yield home.calendarWithName(name)).properties()
</span><span class="cx">         self.assertEqual(len(calendarProperties), 0)
</span><ins>+        # notify is called prior to commit
+        self.assertTrue(&quot;/CalDAV/example.com/home1/&quot; in self.notifierFactory.history)
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Make sure notification fired after commit
-        self.assertTrue(&quot;/CalDAV/example.com/home1/&quot; in self.notifierFactory.history)
-
</del><span class="cx">         # Make sure it's available in a new transaction; i.e. test the commit.
</span><span class="cx">         home = yield self.homeUnderTest()
</span><span class="cx">         self.assertNotIdentical((yield home.calendarWithName(name)), None)
</span><span class="lines">@@ -915,8 +914,7 @@
</span><span class="cx">                 None
</span><span class="cx">             )
</span><span class="cx"> 
</span><del>-        # Make sure notifications are fired after commit
-        yield self.commit()
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -924,6 +922,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home1/calendar_1/&quot;,
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1471,9 +1470,7 @@
</span><span class="cx">         self.assertEquals((yield calendarObject.componentForUser()), component)
</span><span class="cx">         self.assertEquals((yield calendarObject.getMetadata()), metadata)
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notifications fire after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -1481,6 +1478,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home1/calendar_1/&quot;,
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1591,9 +1589,7 @@
</span><span class="cx">         calendarObject = yield calendar1.calendarObjectWithName(&quot;1.ics&quot;)
</span><span class="cx">         self.assertEquals((yield calendarObject.componentForUser()), component)
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notification fired after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -1601,6 +1597,7 @@
</span><span class="cx">                 &quot;/CalDAV/example.com/home1/calendar_1/&quot;,
</span><span class="cx">             ])
</span><span class="cx">         )
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def checkPropertiesMethod(self, thunk):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_implicit.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_implicit.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_implicit.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -478,9 +478,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_validation_preservePrivateComments(self):
</del><ins>+    def test_validation_noPreservePrivateComments(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        Test that resource private comments are restored.
</del><ins>+        Test that attendee private comments are no longer restored.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         data1 = &quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="lines">@@ -524,12 +524,65 @@
</span><span class="cx">         calendar_resource = (yield self.calendarObjectUnderTest(name=&quot;test.ics&quot;, home=&quot;user01&quot;,))
</span><span class="cx">         calendar1 = (yield calendar_resource.component())
</span><span class="cx">         calendar1 = str(calendar1).replace(&quot;\r\n &quot;, &quot;&quot;)
</span><del>-        self.assertTrue(&quot;X-CALENDARSERVER-PRIVATE-COMMENT:My Comment&quot; in calendar1)
</del><ins>+        self.assertFalse(&quot;X-CALENDARSERVER-PRIVATE-COMMENT:My Comment&quot; in calendar1)
</ins><span class="cx">         self.assertTrue(&quot;SUMMARY:Changed&quot; in calendar1)
</span><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_validation_preserveOrganizerPrivateComments(self):
+        &quot;&quot;&quot;
+        Test that organizer private comments are restored.
+        &quot;&quot;&quot;
+
+        data1 = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=&quot;urn:uuid:user01&quot;;
+ X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        calendar_collection = (yield self.calendarUnderTest(home=&quot;user01&quot;))
+        calendar = Component.fromString(data1)
+        yield calendar_collection.createCalendarObjectWithName(&quot;test.ics&quot;, calendar)
+        yield self.commit()
+
+        data2 = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-organizer
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Changed
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        calendar_resource = (yield self.calendarObjectUnderTest(name=&quot;test.ics&quot;, home=&quot;user01&quot;,))
+        calendar = Component.fromString(data2)
+        txn = self.transactionUnderTest()
+        txn._authz_uid = &quot;user01&quot;
+        yield calendar_resource.setComponent(calendar)
+        yield self.commit()
+
+        calendar_resource = (yield self.calendarObjectUnderTest(name=&quot;test.ics&quot;, home=&quot;user01&quot;,))
+        calendar1 = (yield calendar_resource.component())
+        calendar1 = str(calendar1).replace(&quot;\r\n &quot;, &quot;&quot;)
+        self.assertTrue(&quot;X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=\&quot;urn:uuid:user01\&quot;;X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment&quot; in calendar1)
+        self.assertTrue(&quot;SUMMARY:Changed&quot; in calendar1)
+        yield self.commit()
+
+
+    @inlineCallbacks
</ins><span class="cx">     def test_validation_replaceMissingToDoProperties_OrganizerAttendee(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Test that missing scheduling properties in VTODOs are recovered.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/test_util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -526,16 +526,25 @@
</span><span class="cx">                 &quot;different-name&quot;: self.sampleEvent(&quot;other-uid&quot;, &quot;tgt other&quot;),
</span><span class="cx">             },
</span><span class="cx">         )
</span><ins>+
</ins><span class="cx">         txn = self.transactionUnderTest()
</span><del>-        c1 = yield txn.calendarHomeWithUID(&quot;conflict1&quot;)
</del><span class="cx">         c2 = yield txn.calendarHomeWithUID(&quot;conflict2&quot;)
</span><span class="cx">         otherCal = yield c2.createCalendarWithName(&quot;othercal&quot;)
</span><del>-        otherCal.createCalendarObjectWithName(
</del><ins>+        yield otherCal.createCalendarObjectWithName(
</ins><span class="cx">             &quot;some-name&quot;, Component.fromString(
</span><span class="cx">                 self.sampleEvent(&quot;oc&quot;, &quot;target calendar&quot;)[0]
</span><span class="cx">             )
</span><span class="cx">         )
</span><ins>+        yield self.commit()
+
+        txn = self.transactionUnderTest()
+        c1 = yield txn.calendarHomeWithUID(&quot;conflict1&quot;)
+        c2 = yield txn.calendarHomeWithUID(&quot;conflict2&quot;)
</ins><span class="cx">         yield migrateHome(c1, c2, merge=True)
</span><ins>+        yield self.commit()
+
+        txn = self.transactionUnderTest()
+        c2 = yield txn.calendarHomeWithUID(&quot;conflict2&quot;)
</ins><span class="cx">         targetCal = yield c2.calendarWithName(&quot;conflicted&quot;)
</span><span class="cx">         yield self.checkSummary(&quot;same-name&quot;, &quot;target&quot;, targetCal)
</span><span class="cx">         yield self.checkSummary(&quot;different-name&quot;, &quot;tgt other&quot;, targetCal)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavdatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/datastore/test/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -61,7 +61,6 @@
</span><span class="cx">         fullName,
</span><span class="cx">         calendarUserAddresses,
</span><span class="cx">         cutype=&quot;INDIVIDUAL&quot;,
</span><del>-        locallyHosted=True,
</del><span class="cx">         thisServer=True,
</span><span class="cx">     ):
</span><span class="cx"> 
</span><span class="lines">@@ -72,7 +71,6 @@
</span><span class="cx">         self.displayName = self.fullName if self.fullName else self.shortNames[0]
</span><span class="cx">         self.calendarUserAddresses = calendarUserAddresses
</span><span class="cx">         self.cutype = cutype
</span><del>-        self._locallyHosted = locallyHosted
</del><span class="cx">         self._thisServer = thisServer
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -92,10 +90,6 @@
</span><span class="cx">         return cua
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def locallyHosted(self):
-        return self._locallyHosted
-
-
</del><span class="cx">     def thisServer(self):
</span><span class="cx">         return self._thisServer
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcaldavicalendardirectoryservicepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/caldav/icalendardirectoryservice.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/caldav/icalendardirectoryservice.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/caldav/icalendardirectoryservice.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -60,18 +60,9 @@
</span><span class="cx">         @rtype: C{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def locallyHosted(): #@NoSelf
-        &quot;&quot;&quot;
-        Indicates whether the record is host on this specific server &quot;pod&quot;.
-
-        @return: C{True} if locally hosted.
-        @rtype: C{bool}
-        &quot;&quot;&quot;
-
</del><span class="cx">     def thisServer(): #@NoSelf
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Indicates whether the record is hosted on this server or another &quot;pod&quot;
-        that hosts the same directory service.
</del><ins>+        Indicates whether the record is hosted on this server &quot;pod&quot;.
</ins><span class="cx"> 
</span><span class="cx">         @return: C{True} if hosted by this service.
</span><span class="cx">         @rtype: C{bool}
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcarddavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/carddav/datastore/test/common.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/carddav/datastore/test/common.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/carddav/datastore/test/common.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -371,11 +371,10 @@
</span><span class="cx">         #self.assertIdentical((yield home.addressbookWithName(name)), None)
</span><span class="cx">         yield home.removeAddressBookWithName(name)
</span><span class="cx">         self.assertNotIdentical((yield home.addressbookWithName(name)), None)
</span><ins>+        # notify is called prior to commit
+        self.assertTrue(&quot;/CardDAV/example.com/home1/&quot; in self.notifierFactory.history)
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Make sure notification fired after commit
-        self.assertTrue(&quot;/CardDAV/example.com/home1/&quot; in self.notifierFactory.history)
-
</del><span class="cx">         # Make sure it's available in a new transaction; i.e. test the commit.
</span><span class="cx">         home = yield self.homeUnderTest()
</span><span class="cx">         self.assertNotIdentical((yield home.addressbookWithName(name)), None)
</span><span class="lines">@@ -396,9 +395,7 @@
</span><span class="cx">             ab = yield home.addressbookWithName(name)
</span><span class="cx">             self.assertEquals((yield ab.listAddressBookObjects()), [])
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notification fired after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -407,7 +404,9 @@
</span><span class="cx">             ])
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_removeAddressBookWithName_absent(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -530,8 +529,6 @@
</span><span class="cx">                 (yield addressbook.addressbookObjectWithName(name)), None
</span><span class="cx">             )
</span><span class="cx"> 
</span><del>-        # Make sure notifications are fired after commit
-        yield self.commit()
</del><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -692,9 +689,7 @@
</span><span class="cx">         addressbookObject = yield addressbook1.addressbookObjectWithName(name)
</span><span class="cx">         self.assertEquals((yield addressbookObject.component()), component)
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notifications fire after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -703,7 +698,9 @@
</span><span class="cx">             ])
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_createAddressBookObjectWithName_exists(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -808,9 +805,7 @@
</span><span class="cx">         addressbookObject = yield addressbook1.addressbookObjectWithName(&quot;1.vcf&quot;)
</span><span class="cx">         self.assertEquals((yield addressbookObject.component()), component)
</span><span class="cx"> 
</span><del>-        yield self.commit()
-
-        # Make sure notification fired after commit
</del><ins>+        # notify is called prior to commit
</ins><span class="cx">         self.assertEquals(
</span><span class="cx">             set(self.notifierFactory.history),
</span><span class="cx">             set([
</span><span class="lines">@@ -819,7 +814,9 @@
</span><span class="cx">             ])
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def checkPropertiesMethod(self, thunk):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Verify that the given object has a properties method that returns an
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/file.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/file.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/file.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -926,6 +926,7 @@
</span><span class="cx">         return (self._notifierPrefix, self.uid(),)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def notifyChanged(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Trigger a notification of a change
</span><span class="lines">@@ -933,8 +934,14 @@
</span><span class="cx"> 
</span><span class="cx">         # Only send one set of change notifications per transaction
</span><span class="cx">         if self._notifiers and not self._transaction.isNotifiedAlready(self):
</span><del>-            for notifier in self._notifiers.values():
</del><ins>+            # cache notifiers run in post commit
+            notifier = self._notifiers.get(&quot;cache&quot;, None)
+            if notifier:
</ins><span class="cx">                 self._transaction.postCommit(notifier.notify)
</span><ins>+            # push notifiers add their work items immediately
+            notifier = self._notifiers.get(&quot;push&quot;, None)
+            if notifier:
+                yield notifier.notify(self._transaction)
</ins><span class="cx">             self._transaction.notificationAddedForObject(self)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1272,6 +1279,7 @@
</span><span class="cx">         return self.ownerHome().notifierID()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
</ins><span class="cx">     def notifyChanged(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Trigger a notification of a change
</span><span class="lines">@@ -1279,8 +1287,14 @@
</span><span class="cx"> 
</span><span class="cx">         # Only send one set of change notifications per transaction
</span><span class="cx">         if self._notifiers and not self._transaction.isNotifiedAlready(self):
</span><del>-            for notifier in self._notifiers.values():
</del><ins>+            # cache notifiers run in post commit
+            notifier = self._notifiers.get(&quot;cache&quot;, None)
+            if notifier:
</ins><span class="cx">                 self._transaction.postCommit(notifier.notify)
</span><ins>+            # push notifiers add their work items immediately
+            notifier = self._notifiers.get(&quot;push&quot;, None)
+            if notifier:
+                yield notifier.notify(self._transaction)
</ins><span class="cx">             self._transaction.notificationAddedForObject(self)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -29,9 +29,10 @@
</span><span class="cx"> 
</span><span class="cx"> from pycalendar.datetime import DateTime
</span><span class="cx"> 
</span><del>-from twext.enterprise.dal.syntax import \
-    Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction, \
-    Select, Update, ColumnSyntax, TableSyntax, Upper, Count, ALL_COLUMNS, Sum
</del><ins>+from twext.enterprise.dal.syntax import (
+    Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction,
+    Select, Update, ColumnSyntax, TableSyntax, Upper, Count, ALL_COLUMNS, Sum,
+    DatabaseLock, DatabaseUnlock)
</ins><span class="cx"> from twext.enterprise.ienterprise import AlreadyFinishedError
</span><span class="cx"> from twext.enterprise.queue import LocalQueuer
</span><span class="cx"> from twext.enterprise.util import parseSQLTimestamp
</span><span class="lines">@@ -314,6 +315,7 @@
</span><span class="cx">         self.label = label
</span><span class="cx">         self.logFileName = logFileName
</span><span class="cx">         self.statements = []
</span><ins>+        self.startTime = time.time()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def startStatement(self, sql, args):
</span><span class="lines">@@ -329,7 +331,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         args = [&quot;%s&quot; % (arg,) for arg in args]
</span><span class="cx">         args = [((arg[:10] + &quot;...&quot;) if len(arg) &gt; 40 else arg) for arg in args]
</span><del>-        self.statements.append([&quot;%s %s&quot; % (sql, args,), 0, 0])
</del><ins>+        self.statements.append([&quot;%s %s&quot; % (sql, args,), 0, 0, 0])
</ins><span class="cx">         return len(self.statements) - 1, time.time()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -343,8 +345,10 @@
</span><span class="cx">         @type rows: C{int}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         index, tstamp = context
</span><ins>+        t = time.time()
</ins><span class="cx">         self.statements[index][1] = len(rows) if rows else 0
</span><del>-        self.statements[index][2] = time.time() - tstamp
</del><ins>+        self.statements[index][2] = t - tstamp
+        self.statements[index][3] = t
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def printReport(self):
</span><span class="lines">@@ -352,19 +356,28 @@
</span><span class="cx">         Print a report of all the SQL statements executed to date.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+        total_statements = len(self.statements)
+        total_rows = sum([statement[1] for statement in self.statements])
+        total_time = sum([statement[2] for statement in self.statements]) * 1000.0
+
</ins><span class="cx">         toFile = StringIO()
</span><span class="cx">         toFile.write(&quot;*** SQL Stats ***\n&quot;)
</span><span class="cx">         toFile.write(&quot;\n&quot;)
</span><span class="cx">         toFile.write(&quot;Label: %s\n&quot; % (self.label,))
</span><span class="cx">         toFile.write(&quot;Unique statements: %d\n&quot; % (len(set([statement[0] for statement in self.statements]),),))
</span><del>-        toFile.write(&quot;Total statements: %d\n&quot; % (len(self.statements),))
-        toFile.write(&quot;Total rows: %d\n&quot; % (sum([statement[1] for statement in self.statements]),))
-        toFile.write(&quot;Total time (ms): %.3f\n&quot; % (sum([statement[2] for statement in self.statements]) * 1000.0,))
-        for sql, rows, t in self.statements:
</del><ins>+        toFile.write(&quot;Total statements: %d\n&quot; % (total_statements,))
+        toFile.write(&quot;Total rows: %d\n&quot; % (total_rows,))
+        toFile.write(&quot;Total time (ms): %.3f\n&quot; % (total_time,))
+        t_last_end = self.startTime
+        for sql, rows, t_taken, t_end in self.statements:
</ins><span class="cx">             toFile.write(&quot;\n&quot;)
</span><span class="cx">             toFile.write(&quot;SQL: %s\n&quot; % (sql,))
</span><span class="cx">             toFile.write(&quot;Rows: %s\n&quot; % (rows,))
</span><del>-            toFile.write(&quot;Time (ms): %.3f\n&quot; % (t * 1000.0,))
</del><ins>+            toFile.write(&quot;Time (ms): %.3f\n&quot; % (t_taken * 1000.0,))
+            toFile.write(&quot;Idle (ms): %.3f\n&quot; % ((t_end - t_taken - t_last_end) * 1000.0,))
+            toFile.write(&quot;Elapsed (ms): %.3f\n&quot; % ((t_end - self.startTime) * 1000.0,))
+            t_last_end = t_end
+        toFile.write(&quot;Commit (ms): %.3f\n&quot; % ((time.time() - t_last_end) * 1000.0,))
</ins><span class="cx">         toFile.write(&quot;***\n\n&quot;)
</span><span class="cx"> 
</span><span class="cx">         if self.logFileName:
</span><span class="lines">@@ -372,8 +385,10 @@
</span><span class="cx">         else:
</span><span class="cx">             log.error(toFile.getvalue())
</span><span class="cx"> 
</span><ins>+        return (total_statements, total_rows, total_time,)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class CommonStoreTransactionMonitor(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Object that monitors the state of a transaction over time and logs or times out
</span><span class="lines">@@ -483,7 +498,9 @@
</span><span class="cx">         self.iudCount = 0
</span><span class="cx">         self.currentStatement = None
</span><span class="cx"> 
</span><ins>+        self.logItems = {}
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def enqueue(self, workItem, **kw):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Enqueue a L{twext.enterprise.queue.WorkItem} for later execution.
</span><span class="lines">@@ -1029,8 +1046,10 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Commit the transaction and execute any post-commit hooks.
</span><span class="cx">         &quot;&quot;&quot;
</span><ins>+
+        # Do stats logging as a postCommit because there might be some pending preCommit SQL we want to log
</ins><span class="cx">         if self._stats:
</span><del>-            self._stats.printReport()
</del><ins>+            self.postCommit(self.statsReport)
</ins><span class="cx">         return self._sqlTxn.commit()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1041,6 +1060,16 @@
</span><span class="cx">         return self._sqlTxn.abort()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def statsReport(self):
+        &quot;&quot;&quot;
+        Print the stats report and record log items
+        &quot;&quot;&quot;
+        sql_statements, sql_rows, sql_time = self._stats.printReport()
+        self.logItems[&quot;sql-s&quot;] = str(sql_statements)
+        self.logItems[&quot;sql-r&quot;] = str(sql_rows)
+        self.logItems[&quot;sql-t&quot;] = &quot;%.1f&quot; % (sql_time,)
+
+
</ins><span class="cx">     def _oldEventsBase(self, limit):
</span><span class="cx">         ch = schema.CALENDAR_HOME
</span><span class="cx">         co = schema.CALENDAR_OBJECT
</span><span class="lines">@@ -1373,11 +1402,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def acquireUpgradeLock(self):
</span><del>-        return self.execSQL(&quot;select pg_advisory_lock(1)&quot;)
</del><ins>+        return DatabaseLock().on(self)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def releaseUpgradeLock(self):
</span><del>-        return self.execSQL(&quot;select pg_advisory_unlock(1)&quot;)
</del><ins>+        return DatabaseUnlock().on(self)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1415,6 +1444,7 @@
</span><span class="cx">         self._txn = transaction
</span><span class="cx">         self._ownerUID = ownerUID
</span><span class="cx">         self._resourceID = None
</span><ins>+        self._dataVersion = None
</ins><span class="cx">         self._childrenLoaded = False
</span><span class="cx">         self._children = {}
</span><span class="cx">         self._notifiers = None
</span><span class="lines">@@ -1660,6 +1690,23 @@
</span><span class="cx">             yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @classproperty
+    def _dataVersionQuery(cls): #@NoSelf
+        ch = cls._homeSchema
+        return Select(
+            [ch.DATAVERSION], From=ch,
+            Where=ch.RESOURCE_ID == Parameter(&quot;resourceID&quot;)
+        )
+
+
+    @inlineCallbacks
+    def dataVersion(self):
+        if self._dataVersion is None:
+            self._dataVersion = (yield self._dataVersionQuery.on(
+                self._txn, resourceID=self._resourceID))[0][0]
+        returnValue(self._dataVersion)
+
+
</ins><span class="cx">     def name(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Implement L{IDataStoreObject.name} to return the uid.
</span><span class="lines">@@ -2195,6 +2242,7 @@
</span><span class="cx">         the resource has changed.  We ensure we only do this once per object
</span><span class="cx">         per transaction.
</span><span class="cx">         &quot;&quot;&quot;
</span><ins>+
</ins><span class="cx">         if self._txn.isNotifiedAlready(self):
</span><span class="cx">             returnValue(None)
</span><span class="cx">         self._txn.notificationAddedForObject(self)
</span><span class="lines">@@ -2205,8 +2253,14 @@
</span><span class="cx"> 
</span><span class="cx">         # Send notifications
</span><span class="cx">         if self._notifiers:
</span><del>-            for notifier in self._notifiers.values():
</del><ins>+            # cache notifiers run in post commit
+            notifier = self._notifiers.get(&quot;cache&quot;, None)
+            if notifier:
</ins><span class="cx">                 self._txn.postCommit(notifier.notify)
</span><ins>+            # push notifiers add their work items immediately
+            notifier = self._notifiers.get(&quot;push&quot;, None)
+            if notifier:
+                yield notifier.notify(self._txn)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="lines">@@ -2320,16 +2374,20 @@
</span><span class="cx">         raise NotImplementedError()
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @classproperty
-    def _objectNamesSinceRevisionQuery(cls): #@NoSelf
</del><ins>+    @classmethod
+    def _objectNamesSinceRevisionQuery(cls, deleted=True): #@NoSelf
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         DAL query for (resource, deleted-flag)
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         rev = cls._revisionsSchema
</span><del>-        return Select([rev.RESOURCE_NAME, rev.DELETED],
-                      From=rev,
-                      Where=(rev.REVISION &gt; Parameter(&quot;revision&quot;)).And(
-                          rev.RESOURCE_ID == Parameter(&quot;resourceID&quot;)))
</del><ins>+        where = (rev.REVISION &gt; Parameter(&quot;revision&quot;)).And(rev.RESOURCE_ID == Parameter(&quot;resourceID&quot;))
+        if not deleted:
+            where = where.And(rev.DELETED == False)
+        return Select(
+            [rev.RESOURCE_NAME, rev.DELETED],
+            From=rev,
+            Where=where,
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def resourceNamesSinceToken(self, token):
</span><span class="lines">@@ -2354,10 +2412,10 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         results = [
</span><del>-            (name if name else &quot;&quot;, deleted)
-            for name, deleted in
-            (yield self._objectNamesSinceRevisionQuery.on(
-                self._txn, revision=revision, resourceID=self._resourceID))
</del><ins>+            (name if name else &quot;&quot;, deleted) for name, deleted in
+                (yield self._objectNamesSinceRevisionQuery(deleted=(revision != 0)).on(
+                    self._txn, revision=revision, resourceID=self._resourceID)
+                )
</ins><span class="cx">         ]
</span><span class="cx">         results.sort(key=lambda x: x[1])
</span><span class="cx"> 
</span><span class="lines">@@ -2435,14 +2493,14 @@
</span><span class="cx">     @classproperty
</span><span class="cx">     def _bumpSyncTokenQuery(cls): #@NoSelf
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        DAL query to change collection sync token.
</del><ins>+        DAL query to change collection sync token. Note this can impact multiple rows if the
+        collection is shared.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         rev = cls._revisionsSchema
</span><span class="cx">         return Update(
</span><span class="cx">             {rev.REVISION: schema.REVISION_SEQ, },
</span><span class="cx">             Where=(rev.RESOURCE_ID == Parameter(&quot;resourceID&quot;)).And
</span><del>-                  (rev.RESOURCE_NAME == None),
-            Return=rev.REVISION
</del><ins>+                  (rev.RESOURCE_NAME == None)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -2451,8 +2509,11 @@
</span><span class="cx"> 
</span><span class="cx">         if not self._txn.isRevisionBumpedAlready(self):
</span><span class="cx">             self._txn.bumpRevisionForObject(self)
</span><del>-            self._syncTokenRevision = (yield self._bumpSyncTokenQuery.on(
-                self._txn, resourceID=self._resourceID))[0][0]
</del><ins>+            yield self._bumpSyncTokenQuery.on(
+                self._txn,
+                resourceID=self._resourceID,
+            )
+            self._syncTokenRevision = None
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="lines">@@ -3012,7 +3073,9 @@
</span><span class="cx">             queryCacher = self._txn._queryCacher
</span><span class="cx">             if queryCacher:
</span><span class="cx">                 cacheKey = queryCacher.keyForObjectWithName(shareeView._home._resourceID, shareeView._name)
</span><del>-                queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</del><ins>+                yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
+                cacheKey = queryCacher.keyForObjectWithResourceID(shareeView._home._resourceID, shareeView._resourceID)
+                yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</ins><span class="cx"> 
</span><span class="cx">             shareeView._name = sharedname[0][0]
</span><span class="cx"> 
</span><span class="lines">@@ -3070,7 +3133,9 @@
</span><span class="cx">             queryCacher = self._txn._queryCacher
</span><span class="cx">             if queryCacher:
</span><span class="cx">                 cacheKey = queryCacher.keyForObjectWithName(shareeHome._resourceID, shareeChild._name)
</span><del>-                queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</del><ins>+                yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
+                cacheKey = queryCacher.keyForObjectWithResourceID(shareeHome._resourceID, shareeChild._resourceID)
+                yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</ins><span class="cx">         else:
</span><span class="cx">             deletedBindName = None
</span><span class="cx"> 
</span><span class="lines">@@ -3335,10 +3400,9 @@
</span><span class="cx">     def invalidateQueryCache(self):
</span><span class="cx">         queryCacher = self._txn._queryCacher
</span><span class="cx">         if queryCacher is not None:
</span><del>-            cacheKey = queryCacher.keyForHomeChildMetaData(self._resourceID)
-            yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
-            cacheKey = queryCacher.keyForObjectWithName(self._home._resourceID, self._name)
-            yield queryCacher.invalidateAfterCommit(self._txn, cacheKey)
</del><ins>+            yield queryCacher.invalidateAfterCommit(self._txn, queryCacher.keyForHomeChildMetaData(self._resourceID))
+            yield queryCacher.invalidateAfterCommit(self._txn, queryCacher.keyForObjectWithName(self._home._resourceID, self._name))
+            yield queryCacher.invalidateAfterCommit(self._txn, queryCacher.keyForObjectWithResourceID(self._home._resourceID, self._resourceID))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -3515,6 +3579,7 @@
</span><span class="cx">             if rows and queryCacher:
</span><span class="cx">                 # Cache the result
</span><span class="cx">                 queryCacher.setAfterCommit(home._txn, cacheKey, rows)
</span><ins>+                queryCacher.setAfterCommit(home._txn, queryCacher.keyForObjectWithResourceID(home._resourceID, rows[0][2]), rows)
</ins><span class="cx"> 
</span><span class="cx">         if not rows:
</span><span class="cx">             returnValue(None)
</span><span class="lines">@@ -3555,8 +3620,24 @@
</span><span class="cx">         @return: an L{CommonHomeChild} or C{None} if no such child
</span><span class="cx">             exists.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        rows = yield cls._bindForResourceIDAndHomeID.on(
-            home._txn, resourceID=resourceID, homeID=home._resourceID)
</del><ins>+
+        rows = None
+        queryCacher = home._txn._queryCacher
+
+        if queryCacher:
+            # Retrieve data from cache
+            cacheKey = queryCacher.keyForObjectWithResourceID(home._resourceID, resourceID)
+            rows = yield queryCacher.get(cacheKey)
+
+        if rows is None:
+            # No cached copy
+            rows = yield cls._bindForResourceIDAndHomeID.on(home._txn, resourceID=resourceID, homeID=home._resourceID)
+
+            if rows and queryCacher:
+                # Cache the result (under both the ID and name values)
+                queryCacher.setAfterCommit(home._txn, cacheKey, rows)
+                queryCacher.setAfterCommit(home._txn, queryCacher.keyForObjectWithName(home._resourceID, rows[0][3]), rows)
+
</ins><span class="cx">         if not rows:
</span><span class="cx">             returnValue(None)
</span><span class="cx"> 
</span><span class="lines">@@ -3737,6 +3818,8 @@
</span><span class="cx">         if queryCacher:
</span><span class="cx">             cacheKey = queryCacher.keyForObjectWithName(self._home._resourceID, oldName)
</span><span class="cx">             yield queryCacher.invalidateAfterCommit(self._home._txn, cacheKey)
</span><ins>+            cacheKey = queryCacher.keyForObjectWithResourceID(self._home._resourceID, self._resourceID)
+            yield queryCacher.invalidateAfterCommit(self._home._txn, cacheKey)
</ins><span class="cx"> 
</span><span class="cx">         yield self._renameQuery.on(self._txn, name=name,
</span><span class="cx">                                    resourceID=self._resourceID,
</span><span class="lines">@@ -3770,6 +3853,8 @@
</span><span class="cx">         if queryCacher:
</span><span class="cx">             cacheKey = queryCacher.keyForObjectWithName(self._home._resourceID, self._name)
</span><span class="cx">             yield queryCacher.invalidateAfterCommit(self._home._txn, cacheKey)
</span><ins>+            cacheKey = queryCacher.keyForObjectWithResourceID(self._home._resourceID, self._resourceID)
+            yield queryCacher.invalidateAfterCommit(self._home._txn, cacheKey)
</ins><span class="cx"> 
</span><span class="cx">         yield self._deletedSyncToken()
</span><span class="cx">         yield self._deleteQuery.on(self._txn, NoSuchHomeChildError,
</span><span class="lines">@@ -4256,8 +4341,14 @@
</span><span class="cx"> 
</span><span class="cx">         # Send notifications
</span><span class="cx">         if self._notifiers:
</span><del>-            for notifier in self._notifiers.values():
</del><ins>+            # cache notifiers run in post commit
+            notifier = self._notifiers.get(&quot;cache&quot;, None)
+            if notifier:
</ins><span class="cx">                 self._txn.postCommit(notifier.notify)
</span><ins>+            # push notifiers add their work items immediately
+            notifier = self._notifiers.get(&quot;push&quot;, None)
+            if notifier:
+                yield notifier.notify(self._txn)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="lines">@@ -4480,7 +4571,7 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def create(cls, parent, name, component, options=None):
</span><span class="cx"> 
</span><del>-        child = (yield cls.objectWithName(parent, name, None))
</del><ins>+        child = (yield parent.objectResourceWithName(name))
</ins><span class="cx">         if child:
</span><span class="cx">             raise ObjectResourceNameAlreadyExistsError(name)
</span><span class="cx"> 
</span><span class="lines">@@ -5077,15 +5168,21 @@
</span><span class="cx">         the resource has changed.  We ensure we only do this once per object
</span><span class="cx">         per transaction.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        yield
</del><span class="cx">         if self._txn.isNotifiedAlready(self):
</span><span class="cx">             returnValue(None)
</span><span class="cx">         self._txn.notificationAddedForObject(self)
</span><span class="cx"> 
</span><span class="cx">         # Send notifications
</span><span class="cx">         if self._notifiers:
</span><del>-            for notifier in self._notifiers.values():
</del><ins>+            # cache notifiers run in post commit
+            notifier = self._notifiers.get(&quot;cache&quot;, None)
+            if notifier:
</ins><span class="cx">                 self._txn.postCommit(notifier.notify)
</span><ins>+            # push notifiers add their work items immediately
+            notifier = self._notifiers.get(&quot;push&quot;, None)
+            if notifier:
+                yield notifier.notify(self._txn)
+
</ins><span class="cx">         returnValue(None)
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemacurrentoracledialectsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current-oracle-dialect.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -366,7 +366,7 @@
</span><span class="cx">     &quot;VALUE&quot; nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
</del><ins>+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '27');
</ins><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
</span><span class="cx"> insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
</span><span class="cx"> create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
</span><span class="lines">@@ -452,9 +452,11 @@
</span><span class="cx">     CALENDAR_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
</del><ins>+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
</ins><span class="cx">     CALENDAR_RESOURCE_ID,
</span><del>-    RESOURCE_NAME
</del><ins>+    RESOURCE_NAME,
+    DELETED,
+    REVISION
</ins><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -467,9 +469,11 @@
</span><span class="cx">     OWNER_HOME_RESOURCE_ID
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</del><ins>+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
</ins><span class="cx">     OWNER_HOME_RESOURCE_ID,
</span><del>-    RESOURCE_NAME
</del><ins>+    RESOURCE_NAME,
+    DELETED,
+    REVISION
</ins><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/current.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -529,8 +529,8 @@
</span><span class="cx"> create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
</span><span class="cx">   on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
</span><span class="cx"> 
</span><del>-create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
-  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
</del><ins>+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx"> 
</span><span class="cx"> create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
</span><span class="cx">   on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
</span><span class="lines">@@ -552,8 +552,8 @@
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
</span><span class="cx">   on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
</span><span class="cx"> 
</span><del>-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
-  on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME);
</del><ins>+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx"> 
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
</span><span class="cx">   on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
</span><span class="lines">@@ -698,6 +698,6 @@
</span><span class="cx">   VALUE                         varchar(255)
</span><span class="cx"> );
</span><span class="cx"> 
</span><del>-insert into CALENDARSERVER values ('VERSION', '26');
</del><ins>+insert into CALENDARSERVER values ('VERSION', '27');
</ins><span class="cx"> insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
</span><span class="cx"> insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaoldoracledialectv26sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/oracle-dialect/v26.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,495 @@
</span><ins>+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence ATTACHMENT_ID_SEQ;
+create sequence REVISION_SEQ;
+create sequence WORKITEM_SEQ;
+create table NODE_INFO (
+    &quot;HOSTNAME&quot; nvarchar2(255),
+    &quot;PID&quot; integer not null,
+    &quot;PORT&quot; integer not null,
+    &quot;TIME&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null, 
+    primary key(&quot;HOSTNAME&quot;, &quot;PORT&quot;)
+);
+
+create table NAMED_LOCK (
+    &quot;LOCK_NAME&quot; nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table CALENDAR (
+    &quot;RESOURCE_ID&quot; integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;DEFAULT_EVENTS&quot; integer default null references CALENDAR on delete set null,
+    &quot;DEFAULT_TASKS&quot; integer default null references CALENDAR on delete set null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;AVAILABILITY&quot; nclob default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references CALENDAR on delete cascade,
+    &quot;SUPPORTED_COMPONENTS&quot; nvarchar2(255) default null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME,
+    &quot;NOTIFICATION_UID&quot; nvarchar2(255),
+    &quot;XML_TYPE&quot; nvarchar2(255),
+    &quot;XML_DATA&quot; nclob,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique(&quot;NOTIFICATION_UID&quot;, &quot;NOTIFICATION_HOME_RESOURCE_ID&quot;)
+);
+
+create table CALENDAR_BIND (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;CALENDAR_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob,
+    &quot;TRANSP&quot; integer default 0 not null,
+    &quot;ALARM_VEVENT_TIMED&quot; nclob default null,
+    &quot;ALARM_VEVENT_ALLDAY&quot; nclob default null,
+    &quot;ALARM_VTODO_TIMED&quot; nclob default null,
+    &quot;ALARM_VTODO_ALLDAY&quot; nclob default null,
+    &quot;TIMEZONE&quot; nclob default null, 
+    primary key(&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_ID&quot;), 
+    unique(&quot;CALENDAR_HOME_RESOURCE_ID&quot;, &quot;CALENDAR_RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_BIND_MODE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_TRANSP (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('opaque', 0);
+insert into CALENDAR_TRANSP (DESCRIPTION, ID) values ('transparent', 1);
+create table CALENDAR_OBJECT (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob,
+    &quot;ICALENDAR_UID&quot; nvarchar2(255),
+    &quot;ICALENDAR_TYPE&quot; nvarchar2(255),
+    &quot;ATTACHMENTS_MODE&quot; integer default 0 not null,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;RECURRANCE_MIN&quot; date,
+    &quot;RECURRANCE_MAX&quot; date,
+    &quot;ACCESS&quot; integer default 0 not null,
+    &quot;SCHEDULE_OBJECT&quot; integer default 0,
+    &quot;SCHEDULE_TAG&quot; nvarchar2(36) default null,
+    &quot;SCHEDULE_ETAGS&quot; nclob default null,
+    &quot;PRIVATE_COMMENTS&quot; integer default 0 not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique(&quot;CALENDAR_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+    &quot;INSTANCE_ID&quot; integer primary key,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer not null references CALENDAR on delete cascade,
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade,
+    &quot;FLOATING&quot; integer not null,
+    &quot;START_DATE&quot; timestamp not null,
+    &quot;END_DATE&quot; timestamp not null,
+    &quot;FBTYPE&quot; integer not null,
+    &quot;TRANSPARENT&quot; integer not null
+);
+
+create table FREE_BUSY_TYPE (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+    &quot;TIME_RANGE_INSTANCE_ID&quot; integer not null references TIME_RANGE on delete cascade,
+    &quot;USER_ID&quot; nvarchar2(255),
+    &quot;TRANSPARENT&quot; integer not null
+);
+
+create table ATTACHMENT (
+    &quot;ATTACHMENT_ID&quot; integer primary key,
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;DROPBOX_ID&quot; nvarchar2(255),
+    &quot;CONTENT_TYPE&quot; nvarchar2(255),
+    &quot;SIZE&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;PATH&quot; nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+    &quot;ATTACHMENT_ID&quot; integer not null references ATTACHMENT on delete cascade,
+    &quot;MANAGED_ID&quot; nvarchar2(255),
+    &quot;CALENDAR_OBJECT_RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade, 
+    primary key(&quot;ATTACHMENT_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;), 
+    unique(&quot;MANAGED_ID&quot;, &quot;CALENDAR_OBJECT_RESOURCE_ID&quot;)
+);
+
+create table RESOURCE_PROPERTY (
+    &quot;RESOURCE_ID&quot; integer not null,
+    &quot;NAME&quot; nvarchar2(255),
+    &quot;VALUE&quot; nclob,
+    &quot;VIEWER_UID&quot; nvarchar2(255), 
+    primary key(&quot;RESOURCE_ID&quot;, &quot;NAME&quot;, &quot;VIEWER_UID&quot;)
+);
+
+create table ADDRESSBOOK_HOME (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; integer not null,
+    &quot;OWNER_UID&quot; nvarchar2(255) unique,
+    &quot;DATAVERSION&quot; integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+    &quot;RESOURCE_ID&quot; integer primary key references ADDRESSBOOK_HOME on delete cascade,
+    &quot;QUOTA_USED_BYTES&quot; integer default 0 not null,
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table SHARED_ADDRESSBOOK_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;ADDRESSBOOK_RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;OWNER_HOME_RESOURCE_ID&quot;), 
+    unique(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;ADDRESSBOOK_RESOURCE_NAME&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT (
+    &quot;RESOURCE_ID&quot; integer primary key,
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;VCARD_TEXT&quot; nclob,
+    &quot;VCARD_UID&quot; nvarchar2(255),
+    &quot;KIND&quot; integer not null,
+    &quot;MD5&quot; nchar(32),
+    &quot;CREATED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;MODIFIED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;), 
+    unique(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;)
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+    &quot;ID&quot; integer primary key,
+    &quot;DESCRIPTION&quot; nvarchar2(16) unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('person', 0);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('group', 1);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('resource', 2);
+insert into ADDRESSBOOK_OBJECT_KIND (DESCRIPTION, ID) values ('location', 3);
+create table ABO_MEMBERS (
+    &quot;GROUP_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ID&quot; integer not null references ADDRESSBOOK_OBJECT, 
+    primary key(&quot;GROUP_ID&quot;, &quot;MEMBER_ID&quot;)
+);
+
+create table ABO_FOREIGN_MEMBERS (
+    &quot;GROUP_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;ADDRESSBOOK_ID&quot; integer not null references ADDRESSBOOK_HOME on delete cascade,
+    &quot;MEMBER_ADDRESS&quot; nvarchar2(255), 
+    primary key(&quot;GROUP_ID&quot;, &quot;MEMBER_ADDRESS&quot;)
+);
+
+create table SHARED_GROUP_BIND (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;GROUP_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+    &quot;GROUP_ADDRESSBOOK_NAME&quot; nvarchar2(255),
+    &quot;BIND_MODE&quot; integer not null,
+    &quot;BIND_STATUS&quot; integer not null,
+    &quot;BIND_REVISION&quot; integer default 0 not null,
+    &quot;MESSAGE&quot; nclob, 
+    primary key(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_RESOURCE_ID&quot;), 
+    unique(&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;GROUP_ADDRESSBOOK_NAME&quot;)
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+    &quot;CALENDAR_HOME_RESOURCE_ID&quot; integer not null references CALENDAR_HOME,
+    &quot;CALENDAR_RESOURCE_ID&quot; integer references CALENDAR,
+    &quot;CALENDAR_NAME&quot; nvarchar2(255) default null,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+    &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; integer not null references ADDRESSBOOK_HOME,
+    &quot;OWNER_HOME_RESOURCE_ID&quot; integer references ADDRESSBOOK_HOME,
+    &quot;ADDRESSBOOK_NAME&quot; nvarchar2(255) default null,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+    &quot;NOTIFICATION_HOME_RESOURCE_ID&quot; integer not null references NOTIFICATION_HOME on delete cascade,
+    &quot;RESOURCE_NAME&quot; nvarchar2(255),
+    &quot;REVISION&quot; integer not null,
+    &quot;DELETED&quot; integer not null, 
+    unique(&quot;NOTIFICATION_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
+);
+
+create table APN_SUBSCRIPTIONS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;RESOURCE_KEY&quot; nvarchar2(255),
+    &quot;MODIFIED&quot; integer not null,
+    &quot;SUBSCRIBER_GUID&quot; nvarchar2(255),
+    &quot;USER_AGENT&quot; nvarchar2(255) default null,
+    &quot;IP_ADDR&quot; nvarchar2(255) default null, 
+    primary key(&quot;TOKEN&quot;, &quot;RESOURCE_KEY&quot;)
+);
+
+create table IMIP_TOKENS (
+    &quot;TOKEN&quot; nvarchar2(255),
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALUID&quot; nvarchar2(255),
+    &quot;ACCESSED&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    primary key(&quot;ORGANIZER&quot;, &quot;ATTENDEE&quot;, &quot;ICALUID&quot;)
+);
+
+create table IMIP_INVITATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;FROM_ADDR&quot; nvarchar2(255),
+    &quot;TO_ADDR&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table IMIP_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;ORGANIZER&quot; nvarchar2(255),
+    &quot;ATTENDEE&quot; nvarchar2(255),
+    &quot;ICALENDAR_TEXT&quot; nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;PUSH_ID&quot; nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+    &quot;WORK_ID&quot; integer primary key not null,
+    &quot;NOT_BEFORE&quot; timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    &quot;RESOURCE_ID&quot; integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+    &quot;NAME&quot; nvarchar2(255) primary key,
+    &quot;VALUE&quot; nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
+create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
+    DEFAULT_EVENTS
+);
+
+create index CALENDAR_HOME_METADAT_d55e5548 on CALENDAR_HOME_METADATA (
+    DEFAULT_TASKS
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+    NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+    CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+    CALENDAR_RESOURCE_ID,
+    ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+    CALENDAR_RESOURCE_ID,
+    RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ICALE_82e731d5 on CALENDAR_OBJECT (
+    ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+    DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+    CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+    CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+    TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+    CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_O_81508484 on ATTACHMENT_CALENDAR_OBJECT (
+    CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
+    OWNER_HOME_RESOURCE_ID
+);
+
+create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
+    ADDRESSBOOK_ID
+);
+
+create index ABO_MEMBERS_MEMBER_ID_8d66adcf on ABO_MEMBERS (
+    MEMBER_ID
+);
+
+create index ABO_FOREIGN_MEMBERS_A_1fd2c5e9 on ABO_FOREIGN_MEMBERS (
+    ADDRESSBOOK_ID
+);
+
+create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
+    GROUP_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_HOME_RESOURCE_ID,
+    CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_RESOURCE_ID,
+    RESOURCE_NAME,
+    DELETED,
+    REVISION
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_RESOURCE_ID,
+    REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
+    ADDRESSBOOK_HOME_RESOURCE_ID,
+    OWNER_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+    OWNER_HOME_RESOURCE_ID,
+    RESOURCE_NAME,
+    DELETED,
+    REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
+    OWNER_HOME_RESOURCE_ID,
+    REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+    NOTIFICATION_HOME_RESOURCE_ID,
+    REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+    RESOURCE_KEY
+);
+
+create index IMIP_TOKENS_TOKEN_e94b918f on IMIP_TOKENS (
+    TOKEN
+);
+
+create index CALENDAR_OBJECT_SPLIT_af71dcda on CALENDAR_OBJECT_SPLITTER_WORK (
+    RESOURCE_ID
+);
+
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaoldpostgresdialectv26sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/old/postgres-dialect/v26.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,700 @@
</span><ins>+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------------
+-- Cluster Bookkeeping --
+-------------------------
+
+-- Information about a process connected to this database.
+
+-- Note that this must match the node info schema in twext.enterprise.queue.
+create table NODE_INFO (
+  HOSTNAME  varchar(255) not null,
+  PID       integer      not null,
+  PORT      integer      not null,
+  TIME      timestamp    not null default timezone('UTC', CURRENT_TIMESTAMP),
+
+  primary key (HOSTNAME, PORT)
+);
+
+-- Unique named locks.  This table should always be empty, but rows are
+-- temporarily created in order to prevent undesirable concurrency.
+create table NAMED_LOCK (
+    LOCK_NAME varchar(255) primary key
+);
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+  RESOURCE_ID      integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  OWNER_UID        varchar(255) not null unique,                                 -- implicit index
+  DATAVERSION      integer      default 0 not null
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+  RESOURCE_ID integer   primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+  RESOURCE_ID              integer     primary key references CALENDAR_HOME on delete cascade, -- implicit index
+  QUOTA_USED_BYTES         integer     default 0 not null,
+  DEFAULT_EVENTS           integer     default null references CALENDAR on delete set null,
+  DEFAULT_TASKS            integer     default null references CALENDAR on delete set null,
+  ALARM_VEVENT_TIMED       text        default null,
+  ALARM_VEVENT_ALLDAY      text        default null,
+  ALARM_VTODO_TIMED        text        default null,
+  ALARM_VTODO_ALLDAY       text        default null,
+  AVAILABILITY             text        default null,
+  CREATED                  timestamp   default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                 timestamp   default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+create index CALENDAR_HOME_METADATA_DEFAULT_EVENTS on
+        CALENDAR_HOME_METADATA(DEFAULT_EVENTS);
+create index CALENDAR_HOME_METADATA_DEFAULT_TASKS on
+        CALENDAR_HOME_METADATA(DEFAULT_TASKS);
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+  RESOURCE_ID           integer      primary key references CALENDAR on delete cascade, -- implicit index
+  SUPPORTED_COMPONENTS  varchar(255) default null,
+  CREATED               timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+  RESOURCE_ID integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  OWNER_UID   varchar(255) not null unique                                 -- implicit index
+);
+
+create table NOTIFICATION (
+  RESOURCE_ID                   integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  NOTIFICATION_HOME_RESOURCE_ID integer      not null references NOTIFICATION_HOME,
+  NOTIFICATION_UID              varchar(255) not null,
+  XML_TYPE                      varchar(255) not null,
+  XML_DATA                      text         not null,
+  MD5                           char(32)     not null,
+  CREATED                       timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+        NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+  CALENDAR_HOME_RESOURCE_ID integer      not null references CALENDAR_HOME,
+  CALENDAR_RESOURCE_ID      integer      not null references CALENDAR on delete cascade,
+  CALENDAR_RESOURCE_NAME    varchar(255) not null,
+  BIND_MODE                 integer      not null, -- enum CALENDAR_BIND_MODE
+  BIND_STATUS               integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION                                integer      default 0 not null,
+  MESSAGE                   text,
+  TRANSP                    integer      default 0 not null, -- enum CALENDAR_TRANSP
+  ALARM_VEVENT_TIMED        text         default null,
+  ALARM_VEVENT_ALLDAY       text         default null,
+  ALARM_VTODO_TIMED         text         default null,
+  ALARM_VTODO_ALLDAY        text         default null,
+  TIMEZONE                  text         default null,
+
+  primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+  unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME)     -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on
+        CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own'  );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+-- Enumeration of transparency
+
+create table CALENDAR_TRANSP (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_TRANSP values (0, 'opaque' );
+insert into CALENDAR_TRANSP values (1, 'transparent');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+  RESOURCE_ID          integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  CALENDAR_RESOURCE_ID integer      not null references CALENDAR on delete cascade,
+  RESOURCE_NAME        varchar(255) not null,
+  ICALENDAR_TEXT       text         not null,
+  ICALENDAR_UID        varchar(255) not null,
+  ICALENDAR_TYPE       varchar(255) not null,
+  ATTACHMENTS_MODE     integer      default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+  DROPBOX_ID           varchar(255),
+  ORGANIZER            varchar(255),
+  RECURRANCE_MIN       date,        -- minimum date that recurrences have been expanded to.
+  RECURRANCE_MAX       date,        -- maximum date that recurrences have been expanded to.
+  ACCESS               integer      default 0 not null,
+  SCHEDULE_OBJECT      boolean      default false,
+  SCHEDULE_TAG         varchar(36)  default null,
+  SCHEDULE_ETAGS       text         default null,
+  PRIVATE_COMMENTS     boolean      default false not null,
+  MD5                  char(32)     not null,
+  CREATED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED             timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique (CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+  -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+  -- calendar objects, this constraint has to be selectively enforced by the
+  -- application layer.
+
+  -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+  CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+  CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ICALENDAR_UID on
+  CALENDAR_OBJECT(ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+  CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, ''             );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public'       );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private'      );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted'   );
+
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+  INSTANCE_ID                 integer        primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+  CALENDAR_RESOURCE_ID        integer        not null references CALENDAR on delete cascade,
+  CALENDAR_OBJECT_RESOURCE_ID integer        not null references CALENDAR_OBJECT on delete cascade,
+  FLOATING                    boolean        not null,
+  START_DATE                  timestamp      not null,
+  END_DATE                    timestamp      not null,
+  FBTYPE                      integer        not null,
+  TRANSPARENT                 boolean        not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+  TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+  TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown'         );
+insert into FREE_BUSY_TYPE values (1, 'free'            );
+insert into FREE_BUSY_TYPE values (2, 'busy'            );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative'  );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+  TIME_RANGE_INSTANCE_ID      integer      not null references TIME_RANGE on delete cascade,
+  USER_ID                     varchar(255) not null,
+  TRANSPARENT                 boolean      not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+  TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+
+----------------
+-- Attachment --
+----------------
+
+create sequence ATTACHMENT_ID_SEQ;
+
+create table ATTACHMENT (
+  ATTACHMENT_ID               integer           primary key default nextval('ATTACHMENT_ID_SEQ'), -- implicit index
+  CALENDAR_HOME_RESOURCE_ID   integer           not null references CALENDAR_HOME,
+  DROPBOX_ID                  varchar(255),
+  CONTENT_TYPE                varchar(255)      not null,
+  SIZE                        integer           not null,
+  MD5                         char(32)          not null,
+  CREATED                     timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                    timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  PATH                        varchar(1024)     not null
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+  ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-- Many-to-many relationship between attachments and calendar objects
+create table ATTACHMENT_CALENDAR_OBJECT (
+  ATTACHMENT_ID                  integer      not null references ATTACHMENT on delete cascade,
+  MANAGED_ID                     varchar(255) not null,
+  CALENDAR_OBJECT_RESOURCE_ID    integer      not null references CALENDAR_OBJECT on delete cascade,
+
+  primary key (ATTACHMENT_ID, CALENDAR_OBJECT_RESOURCE_ID), -- implicit index
+  unique (MANAGED_ID, CALENDAR_OBJECT_RESOURCE_ID) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_OBJECT_CALENDAR_OBJECT_RESOURCE_ID on
+        ATTACHMENT_CALENDAR_OBJECT(CALENDAR_OBJECT_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+  RESOURCE_ID integer      not null, -- foreign key: *.RESOURCE_ID
+  NAME        varchar(255) not null,
+  VALUE       text         not null, -- FIXME: xml?
+  VIEWER_UID  varchar(255),
+
+  primary key (RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+  RESOURCE_ID                                      integer                        primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  ADDRESSBOOK_PROPERTY_STORE_ID        integer              default nextval('RESOURCE_ID_SEQ') not null,         -- implicit index
+  OWNER_UID                                        varchar(255)         not null unique,                                -- implicit index
+  DATAVERSION                                      integer              default 0 not null
+);
+
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+  RESOURCE_ID      integer      primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+  QUOTA_USED_BYTES integer      default 0 not null,
+  CREATED          timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED         timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+-----------------------------
+-- Shared AddressBook Bind --
+-----------------------------
+
+-- Joins sharee ADDRESSBOOK_HOME and owner ADDRESSBOOK_HOME
+
+create table SHARED_ADDRESSBOOK_BIND (
+  ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
+  OWNER_HOME_RESOURCE_ID                            integer              not null references ADDRESSBOOK_HOME on delete cascade,
+  ADDRESSBOOK_RESOURCE_NAME                            varchar(255)         not null,
+  BIND_MODE                                            integer              not null,        -- enum CALENDAR_BIND_MODE
+  BIND_STATUS                                          integer              not null,        -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION                                                           integer              default 0 not null,
+  MESSAGE                                              text,                                  -- FIXME: xml?
+
+  primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
+  unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME)     -- implicit index
+);
+
+create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
+  SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
+
+
+------------------------
+-- AddressBook Object --
+------------------------
+
+create table ADDRESSBOOK_OBJECT (
+  RESOURCE_ID                             integer                   primary key default nextval('RESOURCE_ID_SEQ'),    -- implicit index
+  ADDRESSBOOK_HOME_RESOURCE_ID         integer              not null references ADDRESSBOOK_HOME on delete cascade,
+  RESOURCE_NAME                           varchar(255)         not null,
+  VCARD_TEXT                              text                 not null,
+  VCARD_UID                               varchar(255)         not null,
+  KIND                                                             integer              not null,  -- enum ADDRESSBOOK_OBJECT_KIND
+  MD5                                     char(32)             not null,
+  CREATED                                 timestamp            default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                                timestamp            default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique (ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+  unique (ADDRESSBOOK_HOME_RESOURCE_ID, VCARD_UID)      -- implicit index
+);
+
+
+-----------------------------
+-- AddressBook Object kind --
+-----------------------------
+
+create table ADDRESSBOOK_OBJECT_KIND (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into ADDRESSBOOK_OBJECT_KIND values (0, 'person');
+insert into ADDRESSBOOK_OBJECT_KIND values (1, 'group' );
+insert into ADDRESSBOOK_OBJECT_KIND values (2, 'resource');
+insert into ADDRESSBOOK_OBJECT_KIND values (3, 'location');
+
+
+---------------------------------
+-- Address Book Object Members --
+---------------------------------
+
+create table ABO_MEMBERS (
+    GROUP_ID              integer      not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                  integer      not null references ADDRESSBOOK_HOME on delete cascade,
+    MEMBER_ID             integer      not null references ADDRESSBOOK_OBJECT,                                                -- member AddressBook Object's RESOURCE_ID
+
+    primary key (GROUP_ID, MEMBER_ID) -- implicit index
+);
+
+create index ABO_MEMBERS_ADDRESSBOOK_ID on
+        ABO_MEMBERS(ADDRESSBOOK_ID);
+create index ABO_MEMBERS_MEMBER_ID on
+        ABO_MEMBERS(MEMBER_ID);
+
+------------------------------------------
+-- Address Book Object Foreign Members  --
+------------------------------------------
+
+create table ABO_FOREIGN_MEMBERS (
+    GROUP_ID              integer      not null references ADDRESSBOOK_OBJECT on delete cascade,        -- AddressBook Object's (kind=='group') RESOURCE_ID
+         ADDRESSBOOK_ID                  integer      not null references ADDRESSBOOK_HOME on delete cascade,
+    MEMBER_ADDRESS            varchar(255) not null,                                                                                                         -- member AddressBook Object's 'calendar' address
+
+    primary key (GROUP_ID, MEMBER_ADDRESS) -- implicit index
+);
+
+create index ABO_FOREIGN_MEMBERS_ADDRESSBOOK_ID on
+        ABO_FOREIGN_MEMBERS(ADDRESSBOOK_ID);
+
+-----------------------
+-- Shared Group Bind --
+-----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK_OBJECT (kind == group)
+
+create table SHARED_GROUP_BIND (        
+  ADDRESSBOOK_HOME_RESOURCE_ID                 integer      not null references ADDRESSBOOK_HOME,
+  GROUP_RESOURCE_ID                              integer      not null references ADDRESSBOOK_OBJECT on delete cascade,
+  GROUP_ADDRESSBOOK_NAME                        varchar(255) not null,
+  BIND_MODE                                    integer      not null, -- enum CALENDAR_BIND_MODE
+  BIND_STATUS                                  integer      not null, -- enum CALENDAR_BIND_STATUS
+  BIND_REVISION                                                   integer      default 0 not null,
+  MESSAGE                                      text,                  -- FIXME: xml?
+
+  primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
+  unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME)     -- implicit index
+);
+
+create index SHARED_GROUP_BIND_RESOURCE_ID on
+  SHARED_GROUP_BIND(GROUP_RESOURCE_ID);
+
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+-------------------------------
+-- Calendar Object Revisions --
+-------------------------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+  CALENDAR_HOME_RESOURCE_ID integer      not null references CALENDAR_HOME,
+  CALENDAR_RESOURCE_ID      integer      references CALENDAR,
+  CALENDAR_NAME             varchar(255) default null,
+  RESOURCE_NAME             varchar(255),
+  REVISION                  integer      default nextval('REVISION_SEQ') not null,
+  DELETED                   boolean      not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+
+----------------------------------
+-- AddressBook Object Revisions --
+----------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+  ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
+  OWNER_HOME_RESOURCE_ID                            integer             references ADDRESSBOOK_HOME,
+  ADDRESSBOOK_NAME                                     varchar(255)         default null,
+  RESOURCE_NAME                                        varchar(255),
+  REVISION                                             integer             default nextval('REVISION_SEQ') not null,
+  DELETED                                              boolean              not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+  on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+  on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
+
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+  NOTIFICATION_HOME_RESOURCE_ID integer      not null references NOTIFICATION_HOME on delete cascade,
+  RESOURCE_NAME                 varchar(255),
+  REVISION                      integer      default nextval('REVISION_SEQ') not null,
+  DELETED                       boolean      not null,
+
+  unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+  on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+  TOKEN                         varchar(255) not null,
+  RESOURCE_KEY                  varchar(255) not null,
+  MODIFIED                      integer      not null,
+  SUBSCRIBER_GUID               varchar(255) not null,
+  USER_AGENT                    varchar(255) default null,
+  IP_ADDR                       varchar(255) default null,
+
+  primary key (TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+   on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+   
+-----------------
+-- IMIP Tokens --
+-----------------
+
+create table IMIP_TOKENS (
+  TOKEN                         varchar(255) not null,
+  ORGANIZER                     varchar(255) not null,
+  ATTENDEE                      varchar(255) not null,
+  ICALUID                       varchar(255) not null,
+  ACCESSED                      timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  primary key (ORGANIZER, ATTENDEE, ICALUID) -- implicit index
+);
+
+create index IMIP_TOKENS_TOKEN
+   on IMIP_TOKENS(TOKEN);
+
+   
+----------------
+-- Work Items --
+----------------
+
+create sequence WORKITEM_SEQ;
+
+
+---------------------------
+-- IMIP Inivitation Work --
+---------------------------
+
+create table IMIP_INVITATION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  FROM_ADDR                     varchar(255) not null,
+  TO_ADDR                       varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+
+-----------------------
+-- IMIP Polling Work --
+-----------------------
+
+create table IMIP_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+---------------------
+-- IMIP Reply Work --
+---------------------
+
+create table IMIP_REPLY_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  ORGANIZER                     varchar(255) not null,
+  ATTENDEE                      varchar(255) not null,
+  ICALENDAR_TEXT                text         not null
+);
+
+
+------------------------
+-- Push Notifications --
+------------------------
+
+create table PUSH_NOTIFICATION_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  PUSH_ID                       varchar(255) not null
+);
+
+-----------------
+-- GroupCacher --
+-----------------
+
+create table GROUP_CACHER_POLLING_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+--------------------------
+-- Object Splitter Work --
+--------------------------
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+  WORK_ID                       integer      primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
+  NOT_BEFORE                    timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  RESOURCE_ID                   integer      not null references CALENDAR_OBJECT on delete cascade
+);
+
+create index CALENDAR_OBJECT_SPLITTER_WORK_RESOURCE_ID on
+        CALENDAR_OBJECT_SPLITTER_WORK(RESOURCE_ID);
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+  NAME                          varchar(255) primary key, -- implicit index
+  VALUE                         varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '26');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_19_to_20sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -113,8 +113,12 @@
</span><span class="cx"> -- Alter  ADDRESSBOOK_HOME --
</span><span class="cx"> -----------------------------
</span><span class="cx"> 
</span><ins>+-- This is tricky as we have to create a new not null column and populate it, but we can't do
+-- not null immediately without a default - which we do not want. So we create the column without not null,
+-- do the updates, then add the constraint.
+
</ins><span class="cx"> alter table ADDRESSBOOK_HOME
</span><del>-        add (&quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; integer not null);
</del><ins>+        add (&quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; integer);
</ins><span class="cx"> 
</span><span class="cx"> update ADDRESSBOOK_HOME
</span><span class="cx">         set        ADDRESSBOOK_PROPERTY_STORE_ID = (
</span><span class="lines">@@ -133,8 +137,11 @@
</span><span class="cx">                         ADDRESSBOOK_BIND.BIND_MODE = 0 and         -- CALENDAR_BIND_MODE 'own'
</span><span class="cx">                         ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME = 'addressbook'
</span><span class="cx">           );
</span><del>-        
</del><span class="cx"> 
</span><ins>+alter table ADDRESSBOOK_HOME
+        modify (&quot;ADDRESSBOOK_PROPERTY_STORE_ID&quot; not null);
+
+
</ins><span class="cx"> --------------------------------
</span><span class="cx"> -- change  ADDRESSBOOK_OBJECT --
</span><span class="cx"> --------------------------------
</span><span class="lines">@@ -176,14 +183,15 @@
</span><span class="cx">           
</span><span class="cx"> -- add non null constraints after update and delete are complete
</span><span class="cx"> alter table ADDRESSBOOK_OBJECT
</span><del>-        modify (&quot;KIND&quot; not null,
-            &quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; not null)
-        drop (&quot;ADDRESSBOOK_RESOURCE_ID&quot;);
</del><ins>+        modify (&quot;KIND&quot; not null)
+        modify (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot; not null);
</ins><span class="cx"> 
</span><ins>+alter table ADDRESSBOOK_OBJECT
+        drop column ADDRESSBOOK_RESOURCE_ID cascade constraints;
</ins><span class="cx"> 
</span><span class="cx"> alter table ADDRESSBOOK_OBJECT
</span><span class="cx">         add unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;RESOURCE_NAME&quot;)
</span><del>-            unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;);
</del><ins>+        add unique (&quot;ADDRESSBOOK_HOME_RESOURCE_ID&quot;, &quot;VCARD_UID&quot;);
</ins><span class="cx"> 
</span><span class="cx"> ------------------------------------------
</span><span class="cx"> -- change  ADDRESSBOOK_OBJECT_REVISIONS --
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -18,13 +18,23 @@
</span><span class="cx"> -- Upgrade database schema from VERSION 25 to 26 --
</span><span class="cx"> ---------------------------------------------------
</span><span class="cx"> 
</span><del>--- Calendar home related updates
</del><ins>+-- Replace index
</ins><span class="cx"> 
</span><del>-alter table CALENDAR_HOME_METADATA
- add (&quot;DEFAULT_POLLS&quot; integer default null references CALENDAR on delete set null);
</del><ins>+drop index CALENDAR_OBJECT_REVIS_2643d556;
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_RESOURCE_ID,
+    RESOURCE_NAME,
+    DELETED,
+    REVISION
+);
</ins><span class="cx"> 
</span><del>-create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
-    DEFAULT_POLLS
</del><ins>+
+drop index ADDRESSBOOK_OBJECT_RE_980b9872;
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+    OWNER_HOME_RESOURCE_ID,
+    RESOURCE_NAME,
+    DELETED,
+    REVISION
</ins><span class="cx"> );
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_26_to_27sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_26_to_27.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add (&quot;DEFAULT_POLLS&quot; integer default null references CALENDAR on delete set null);
+
+create index CALENDAR_HOME_METADAT_910264ce on CALENDAR_HOME_METADATA (
+    DEFAULT_POLLS
+);
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_13_to_14sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -26,6 +26,11 @@
</span><span class="cx">  drop column SEEN_BY_OWNER;
</span><span class="cx"> alter table CALENDAR_BIND
</span><span class="cx">  drop column SEEN_BY_SHAREE;
</span><ins>+
+-- Don't allow nulls in the column we are about to constrain
+update CALENDAR_BIND
+        set CALENDAR_RESOURCE_NAME = 'Shared_' || CALENDAR_RESOURCE_ID || '_' || CALENDAR_HOME_RESOURCE_ID
+        where CALENDAR_RESOURCE_NAME is null;
</ins><span class="cx"> alter table CALENDAR_BIND
</span><span class="cx">  alter column CALENDAR_RESOURCE_NAME 
</span><span class="cx">   set not null;
</span><span class="lines">@@ -34,6 +39,11 @@
</span><span class="cx">  drop column SEEN_BY_OWNER;
</span><span class="cx"> alter table ADDRESSBOOK_BIND
</span><span class="cx">  drop column SEEN_BY_SHAREE;
</span><ins>+
+-- Don't allow nulls in the column we are about to constrain
+update ADDRESSBOOK_BIND
+        set ADDRESSBOOK_RESOURCE_NAME = 'Shared_' || ADDRESSBOOK_RESOURCE_ID || '_' || ADDRESSBOOK_HOME_RESOURCE_ID
+        where ADDRESSBOOK_RESOURCE_NAME is null;
</ins><span class="cx"> alter table ADDRESSBOOK_BIND
</span><span class="cx">  alter column ADDRESSBOOK_RESOURCE_NAME
</span><span class="cx">   set not null;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -18,14 +18,15 @@
</span><span class="cx"> -- Upgrade database schema from VERSION 25 to 26 --
</span><span class="cx"> ---------------------------------------------------
</span><span class="cx"> 
</span><del>--- Calendar home related updates
</del><ins>+-- Replace index
+drop index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME;
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx"> 
</span><del>-alter table CALENDAR_HOME_METADATA
- add column DEFAULT_POLLS integer default null references CALENDAR on delete set null;
</del><ins>+drop index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME;
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME_DELETED_REVISION
+  on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME, DELETED, REVISION);
</ins><span class="cx"> 
</span><del>-create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
-        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
-
</del><span class="cx"> -- Now update the version
</span><span class="cx"> -- No data upgrades
</span><span class="cx"> update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_26_to_27sql"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql (0 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_26_to_27.sql        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 26 to 27 --
+---------------------------------------------------
+
+-- Calendar home related updates
+
+alter table CALENDAR_HOME_METADATA
+ add column DEFAULT_POLLS integer default null references CALENDAR on delete set null;
+
+create index CALENDAR_HOME_METADATA_DEFAULT_POLLS on
+        CALENDAR_HOME_METADATA(DEFAULT_POLLS);
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '27' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoresql_tablespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_tables.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_tables.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/sql_tables.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -52,55 +52,39 @@
</span><span class="cx"> # Column aliases, defined so that similar tables (such as CALENDAR_OBJECT and
</span><span class="cx"> # ADDRESSBOOK_OBJECT) can be used according to a polymorphic interface.
</span><span class="cx"> 
</span><del>-schema.CALENDAR_BIND.RESOURCE_NAME = \
-    schema.CALENDAR_BIND.CALENDAR_RESOURCE_NAME
-schema.CALENDAR_BIND.RESOURCE_ID = \
-    schema.CALENDAR_BIND.CALENDAR_RESOURCE_ID
-schema.CALENDAR_BIND.HOME_RESOURCE_ID = \
-    schema.CALENDAR_BIND.CALENDAR_HOME_RESOURCE_ID
-schema.SHARED_ADDRESSBOOK_BIND.RESOURCE_NAME = \
-    schema.SHARED_ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME
-schema.SHARED_ADDRESSBOOK_BIND.RESOURCE_ID = \
-    schema.SHARED_ADDRESSBOOK_BIND.OWNER_HOME_RESOURCE_ID
-schema.SHARED_ADDRESSBOOK_BIND.HOME_RESOURCE_ID = \
-    schema.SHARED_ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID
-schema.SHARED_GROUP_BIND.RESOURCE_NAME = \
-    schema.SHARED_GROUP_BIND.GROUP_ADDRESSBOOK_NAME
-schema.SHARED_GROUP_BIND.RESOURCE_ID = \
-    schema.SHARED_GROUP_BIND.GROUP_RESOURCE_ID
-schema.SHARED_GROUP_BIND.HOME_RESOURCE_ID = \
-    schema.SHARED_GROUP_BIND.ADDRESSBOOK_HOME_RESOURCE_ID
-schema.CALENDAR_OBJECT_REVISIONS.RESOURCE_ID = \
-    schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_RESOURCE_ID
-schema.CALENDAR_OBJECT_REVISIONS.HOME_RESOURCE_ID = \
-    schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_HOME_RESOURCE_ID
-schema.CALENDAR_OBJECT_REVISIONS.COLLECTION_NAME = \
-    schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_NAME
-schema.ADDRESSBOOK_OBJECT_REVISIONS.RESOURCE_ID = \
-    schema.ADDRESSBOOK_OBJECT_REVISIONS.OWNER_HOME_RESOURCE_ID
-schema.ADDRESSBOOK_OBJECT_REVISIONS.HOME_RESOURCE_ID = \
-    schema.ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_HOME_RESOURCE_ID
-schema.ADDRESSBOOK_OBJECT_REVISIONS.COLLECTION_NAME = \
-    schema.ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_NAME
-schema.NOTIFICATION_OBJECT_REVISIONS.HOME_RESOURCE_ID = \
-    schema.NOTIFICATION_OBJECT_REVISIONS.NOTIFICATION_HOME_RESOURCE_ID
-schema.NOTIFICATION_OBJECT_REVISIONS.RESOURCE_ID = \
-    schema.NOTIFICATION_OBJECT_REVISIONS.NOTIFICATION_HOME_RESOURCE_ID
-schema.CALENDAR_OBJECT.TEXT = \
-    schema.CALENDAR_OBJECT.ICALENDAR_TEXT
-schema.CALENDAR_OBJECT.UID = \
-    schema.CALENDAR_OBJECT.ICALENDAR_UID
-schema.CALENDAR_OBJECT.PARENT_RESOURCE_ID = \
-    schema.CALENDAR_OBJECT.CALENDAR_RESOURCE_ID
-schema.ADDRESSBOOK_OBJECT.TEXT = \
-    schema.ADDRESSBOOK_OBJECT.VCARD_TEXT
-schema.ADDRESSBOOK_OBJECT.UID = \
-    schema.ADDRESSBOOK_OBJECT.VCARD_UID
-schema.ADDRESSBOOK_OBJECT.PARENT_RESOURCE_ID = \
-    schema.ADDRESSBOOK_OBJECT.ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+schema.CALENDAR_BIND.RESOURCE_NAME = schema.CALENDAR_BIND.CALENDAR_RESOURCE_NAME
+schema.CALENDAR_BIND.RESOURCE_ID = schema.CALENDAR_BIND.CALENDAR_RESOURCE_ID
+schema.CALENDAR_BIND.HOME_RESOURCE_ID = schema.CALENDAR_BIND.CALENDAR_HOME_RESOURCE_ID
</ins><span class="cx"> 
</span><ins>+schema.SHARED_ADDRESSBOOK_BIND.RESOURCE_NAME = schema.SHARED_ADDRESSBOOK_BIND.ADDRESSBOOK_RESOURCE_NAME
+schema.SHARED_ADDRESSBOOK_BIND.RESOURCE_ID = schema.SHARED_ADDRESSBOOK_BIND.OWNER_HOME_RESOURCE_ID
+schema.SHARED_ADDRESSBOOK_BIND.HOME_RESOURCE_ID = schema.SHARED_ADDRESSBOOK_BIND.ADDRESSBOOK_HOME_RESOURCE_ID
</ins><span class="cx"> 
</span><ins>+schema.SHARED_GROUP_BIND.RESOURCE_NAME = schema.SHARED_GROUP_BIND.GROUP_ADDRESSBOOK_NAME
+schema.SHARED_GROUP_BIND.RESOURCE_ID = schema.SHARED_GROUP_BIND.GROUP_RESOURCE_ID
+schema.SHARED_GROUP_BIND.HOME_RESOURCE_ID = schema.SHARED_GROUP_BIND.ADDRESSBOOK_HOME_RESOURCE_ID
</ins><span class="cx"> 
</span><ins>+schema.CALENDAR_OBJECT_REVISIONS.RESOURCE_ID = schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_RESOURCE_ID
+schema.CALENDAR_OBJECT_REVISIONS.HOME_RESOURCE_ID = schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_HOME_RESOURCE_ID
+schema.CALENDAR_OBJECT_REVISIONS.COLLECTION_NAME = schema.CALENDAR_OBJECT_REVISIONS.CALENDAR_NAME
+
+schema.ADDRESSBOOK_OBJECT_REVISIONS.RESOURCE_ID = schema.ADDRESSBOOK_OBJECT_REVISIONS.OWNER_HOME_RESOURCE_ID
+schema.ADDRESSBOOK_OBJECT_REVISIONS.HOME_RESOURCE_ID = schema.ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_HOME_RESOURCE_ID
+schema.ADDRESSBOOK_OBJECT_REVISIONS.COLLECTION_NAME = schema.ADDRESSBOOK_OBJECT_REVISIONS.ADDRESSBOOK_NAME
+
+schema.NOTIFICATION_OBJECT_REVISIONS.HOME_RESOURCE_ID = schema.NOTIFICATION_OBJECT_REVISIONS.NOTIFICATION_HOME_RESOURCE_ID
+schema.NOTIFICATION_OBJECT_REVISIONS.RESOURCE_ID = schema.NOTIFICATION_OBJECT_REVISIONS.NOTIFICATION_HOME_RESOURCE_ID
+
+schema.CALENDAR_OBJECT.TEXT = schema.CALENDAR_OBJECT.ICALENDAR_TEXT
+schema.CALENDAR_OBJECT.UID = schema.CALENDAR_OBJECT.ICALENDAR_UID
+schema.CALENDAR_OBJECT.PARENT_RESOURCE_ID = schema.CALENDAR_OBJECT.CALENDAR_RESOURCE_ID
+
+schema.ADDRESSBOOK_OBJECT.TEXT = schema.ADDRESSBOOK_OBJECT.VCARD_TEXT
+schema.ADDRESSBOOK_OBJECT.UID = schema.ADDRESSBOOK_OBJECT.VCARD_UID
+schema.ADDRESSBOOK_OBJECT.PARENT_RESOURCE_ID = schema.ADDRESSBOOK_OBJECT.ADDRESSBOOK_HOME_RESOURCE_ID
+
+
+
</ins><span class="cx"> def _combine(**kw):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Combine two table dictionaries used in a join to produce a single dictionary
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/test/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/test/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/test/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -726,7 +726,7 @@
</span><span class="cx">         return &quot;/%s/%s/%s/&quot; % (prefix, self.hostname, id)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def send(self, prefix, id):
</del><ins>+    def send(self, prefix, id, txn):
</ins><span class="cx">         self.history.append(self.pushKeyForId(prefix, id))
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqltesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/test/test_upgrade.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/test/test_upgrade.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/test/test_upgrade.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -25,8 +25,8 @@
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx"> from txdav.common.datastore.sql_dump import dumpSchema
</span><span class="cx"> from txdav.common.datastore.test.util import theStoreBuilder, StubNotifierFactory
</span><del>-from txdav.common.datastore.upgrade.sql.upgrade import UpgradeDatabaseSchemaStep, \
-    UpgradeDatabaseAddressBookDataStep, UpgradeDatabaseCalendarDataStep
</del><ins>+from txdav.common.datastore.upgrade.sql.upgrade import (
+    UpgradeDatabaseSchemaStep, UpgradeDatabaseAddressBookDataStep, UpgradeDatabaseCalendarDataStep, NotAllowedToUpgrade)
</ins><span class="cx"> import re
</span><span class="cx"> 
</span><span class="cx"> class SchemaUpgradeTests(TestCase):
</span><span class="lines">@@ -215,12 +215,12 @@
</span><span class="cx">         old_version = yield _loadVersion()
</span><span class="cx">         try:
</span><span class="cx">             yield upgrader.databaseUpgrade()
</span><del>-        except RuntimeError:
</del><ins>+        except NotAllowedToUpgrade:
</ins><span class="cx">             pass
</span><span class="cx">         except Exception:
</span><del>-            self.fail(&quot;RuntimeError not raised&quot;)
</del><ins>+            self.fail(&quot;NotAllowedToUpgrade not raised&quot;)
</ins><span class="cx">         else:
</span><del>-            self.fail(&quot;RuntimeError not raised&quot;)
</del><ins>+            self.fail(&quot;NotAllowedToUpgrade not raised&quot;)
</ins><span class="cx">         new_version = yield _loadVersion()
</span><span class="cx">         yield _unloadOldSchema()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrade.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrade.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrade.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -74,11 +74,15 @@
</span><span class="cx">         yield sqlTxn.commit()
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def stepWithFailure(self, failure):
-        return self.stepWithResult(None)
</del><span class="cx"> 
</span><ins>+class NotAllowedToUpgrade(Exception):
+    &quot;&quot;&quot;
+    Exception indicating an upgrade is needed but we're not configured to
+    perform it.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> class UpgradeDatabaseCoreStep(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Base class for either schema or data upgrades on the database.
</span><span class="lines">@@ -136,8 +140,7 @@
</span><span class="cx">             self.log.error(msg)
</span><span class="cx">             raise RuntimeError(msg)
</span><span class="cx">         elif self.failIfUpgradeNeeded:
</span><del>-                # TODO: change this exception to be upgrade-specific
-            raise RuntimeError(&quot;Database upgrade is needed but not allowed.&quot;)
</del><ins>+            raise NotAllowedToUpgrade()
</ins><span class="cx">         else:
</span><span class="cx">             self.sqlStore.setUpgrading(True)
</span><span class="cx">             yield self.upgradeVersion(actual_version, required_version, dialect)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradesaddressbook_upgrade_from_1_to_2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -22,7 +22,8 @@
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.common.datastore.sql_tables import _ABO_KIND_GROUP, schema
</span><span class="cx"> from txdav.common.datastore.upgrade.sql.upgrades.util import updateAddressBookDataVersion, \
</span><del>-    doToEachHomeNotAtVersion, removeProperty, cleanPropertyStore
</del><ins>+    doToEachHomeNotAtVersion, removeProperty, cleanPropertyStore, \
+    logUpgradeStatus
</ins><span class="cx"> from txdav.xml import element
</span><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="lines">@@ -73,14 +74,20 @@
</span><span class="cx">                 #update rest
</span><span class="cx">                 yield abObject.setComponent(component)
</span><span class="cx"> 
</span><ins>+    logUpgradeStatus(&quot;Starting Addressbook Populate Members&quot;)
+
</ins><span class="cx">     # Do this to each calendar home not already at version 2
</span><del>-    yield doToEachHomeNotAtVersion(sqlStore, schema.ADDRESSBOOK_HOME, UPGRADE_TO_VERSION, doIt)
</del><ins>+    yield doToEachHomeNotAtVersion(sqlStore, schema.ADDRESSBOOK_HOME, UPGRADE_TO_VERSION, doIt, &quot;Populate Members&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeResourceType(sqlStore):
</span><ins>+    logUpgradeStatus(&quot;Starting Addressbook Remove Resource Type&quot;)
+
</ins><span class="cx">     sqlTxn = sqlStore.newTransaction()
</span><span class="cx">     yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceType))
</span><span class="cx">     yield sqlTxn.commit()
</span><span class="cx">     yield cleanPropertyStore()
</span><ins>+
+    logUpgradeStatus(&quot;End Addressbook Remove Resource Type&quot;)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_1_to_2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -16,12 +16,16 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> from twext.enterprise.dal.syntax import Update
</span><del>-from txdav.xml.parser import WebDAVDocument
</del><ins>+
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><ins>+
</ins><span class="cx"> from twistedcaldav import caldavxml
</span><ins>+
</ins><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty,\
</span><del>-    removeProperty, updateCalendarDataVersion, doToEachHomeNotAtVersion
</del><ins>+    removeProperty, updateCalendarDataVersion, doToEachHomeNotAtVersion, \
+    logUpgradeStatus, logUpgradeError
+from txdav.xml.parser import WebDAVDocument
</ins><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Calendar data upgrade from database version 1 to 2
</span><span class="lines">@@ -50,9 +54,14 @@
</span><span class="cx">     extracting the new format value from the XML property.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    logUpgradeStatus(&quot;Starting Move supported-component-set&quot;)
+
</ins><span class="cx">     sqlTxn = sqlStore.newTransaction()
</span><span class="cx">     try:
</span><ins>+        calendar_rid = None
</ins><span class="cx">         rows = (yield rowsForProperty(sqlTxn, caldavxml.SupportedCalendarComponentSet))
</span><ins>+        total = len(rows)
+        count = 0
</ins><span class="cx">         for calendar_rid, value in rows:
</span><span class="cx">             prop = WebDAVDocument.fromString(value).root_element
</span><span class="cx">             supported_components = &quot;,&quot;.join(sorted([comp.attributes[&quot;name&quot;].upper() for comp in prop.children]))
</span><span class="lines">@@ -63,11 +72,19 @@
</span><span class="cx">                 },
</span><span class="cx">                 Where=(meta.RESOURCE_ID == calendar_rid)
</span><span class="cx">             ).on(sqlTxn)
</span><ins>+            count += 1
+            logUpgradeStatus(&quot;Move supported-component-set&quot;, count, total)
</ins><span class="cx"> 
</span><span class="cx">         yield removeProperty(sqlTxn, caldavxml.SupportedCalendarComponentSet)
</span><span class="cx">         yield sqlTxn.commit()
</span><ins>+
+        logUpgradeStatus(&quot;End Move supported-component-set&quot;)
</ins><span class="cx">     except RuntimeError:
</span><span class="cx">         yield sqlTxn.abort()
</span><ins>+        logUpgradeError(
+            &quot;Move supported-component-set&quot;,
+            &quot;Last calendar: {}&quot;.format(calendar_rid)
+        )
</ins><span class="cx">         raise
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -86,5 +103,7 @@
</span><span class="cx">         home = yield txn.calendarHomeWithResourceID(homeResourceID)
</span><span class="cx">         yield home.splitCalendars()
</span><span class="cx"> 
</span><ins>+    logUpgradeStatus(&quot;Starting Split Calendars&quot;)
+
</ins><span class="cx">     # Do this to each calendar home not already at version 2
</span><del>-    yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, doIt)
</del><ins>+    yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, doIt, &quot;Split Calendars&quot;)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -15,19 +15,17 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from twext.enterprise.dal.syntax import Select, Delete, Parameter
-
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav import caldavxml, customxml
</span><span class="cx"> 
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><del>-from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
-from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
-    updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore
-from txdav.xml.parser import WebDAVDocument
</del><ins>+from txdav.caldav.icalendarstore import InvalidDefaultCalendar
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateCalendarDataVersion, \
+    removeProperty, cleanPropertyStore, logUpgradeStatus, doToEachHomeNotAtVersion
</ins><span class="cx"> from txdav.xml import element
</span><del>-from twisted.python.failure import Failure
</del><ins>+from twistedcaldav.config import config
</ins><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Data upgrade from database version 3 to 4
</span><span class="lines">@@ -41,167 +39,114 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Do the required upgrade steps.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    yield moveDefaultCalendarProperties(sqlStore)
-    yield moveCalendarTranspProperties(sqlStore)
-    yield moveDefaultAlarmProperties(sqlStore)
-    yield removeResourceType(sqlStore)
</del><ins>+    yield updateCalendarHomes(sqlStore, config.UpgradeHomePrefix)
</ins><span class="cx"> 
</span><del>-    # Always bump the DB value
-    yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
-    yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
</del><ins>+    # Don't do remaining upgrade if we are only process a subset of the homes
+    if not config.UpgradeHomePrefix:
+        yield removeResourceType(sqlStore)
</ins><span class="cx"> 
</span><ins>+        # Always bump the DB value
+        yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveDefaultCalendarProperties(sqlStore):
</del><ins>+def updateCalendarHomes(sqlStore, prefix=None):
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
-    RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
-    the new value from the XML property.
</del><ins>+    For each calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    meta = schema.CALENDAR_HOME_METADATA
-    yield _processDefaultCalendarProperty(sqlStore, caldavxml.ScheduleDefaultCalendarURL, meta.DEFAULT_EVENTS)
-    yield _processDefaultCalendarProperty(sqlStore, customxml.ScheduleDefaultTasksURL, meta.DEFAULT_TASKS)
</del><ins>+    yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, updateCalendarHome, &quot;Update Calendar Home&quot;, filterOwnerUID=prefix)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def _processDefaultCalendarProperty(sqlStore, propname, colname):
</del><ins>+def updateCalendarHome(txn, homeResourceID):
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
-
-    Since the number of calendar homes may well be large, we need to do this in batches.
</del><ins>+    For this calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    cb = schema.CALENDAR_BIND
-    rp = schema.RESOURCE_PROPERTY
</del><ins>+    home = yield txn.calendarHomeWithResourceID(homeResourceID)
+    yield moveDefaultCalendarProperties(home)
+    yield moveCalendarTranspProperties(home)
+    yield moveDefaultAlarmProperties(home)
+    yield cleanPropertyStore()
</ins><span class="cx"> 
</span><del>-    try:
-        while True:
-            sqlTxn = sqlStore.newTransaction()
-            rows = (yield rowsForProperty(sqlTxn, propname, batch=BATCH_SIZE))
-            if len(rows) == 0:
-                yield sqlTxn.commit()
-                break
-            delete_ids = []
-            for inbox_rid, value in rows:
-                delete_ids.append(inbox_rid)
-                ids = yield Select(
-                    [cb.CALENDAR_HOME_RESOURCE_ID, ],
-                    From=cb,
-                    Where=cb.CALENDAR_RESOURCE_ID == inbox_rid,
-                ).on(sqlTxn)
-                if len(ids) &gt; 0:
</del><span class="cx"> 
</span><del>-                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
-                    if calendarHome is not None:
</del><span class="cx"> 
</span><del>-                        prop = WebDAVDocument.fromString(value).root_element
-                        defaultCalendar = str(prop.children[0])
-                        parts = defaultCalendar.split(&quot;/&quot;)
-                        if len(parts) == 5:
</del><ins>+@inlineCallbacks
+def moveDefaultCalendarProperties(home):
+    &quot;&quot;&quot;
+    Need to move any the CalDAV:default-calendar and CS:default-tasks properties in the
+    RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
+    the new value from the XML property.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><del>-                            calendarName = parts[-1]
-                            calendarHomeUID = parts[-2]
-                            expectedHome = (yield sqlTxn.calendarHomeWithUID(calendarHomeUID))
-                            if expectedHome is not None and expectedHome.id() == calendarHome.id():
</del><ins>+    yield _processDefaultCalendarProperty(home, caldavxml.ScheduleDefaultCalendarURL)
+    yield _processDefaultCalendarProperty(home, customxml.ScheduleDefaultTasksURL)
</ins><span class="cx"> 
</span><del>-                                calendar = (yield calendarHome.calendarWithName(calendarName))
-                                if calendar is not None:
-                                    if propname == caldavxml.ScheduleDefaultCalendarURL:
-                                        ctype = &quot;VEVENT&quot;
-                                    elif propname == customxml.ScheduleDefaultTasksURL:
-                                        ctype = &quot;VTODO&quot;
-                                    yield calendarHome.setDefaultCalendar(calendar, ctype)
</del><span class="cx"> 
</span><del>-            # Always delete the rows so that batch processing works correctly
-            yield Delete(
-                From=rp,
-                Where=(rp.RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(delete_ids)))).And
-                      (rp.NAME == PropertyName.fromElement(propname).toString()),
-            ).on(sqlTxn, ids=delete_ids)
</del><span class="cx"> 
</span><del>-            yield sqlTxn.commit()
</del><ins>+@inlineCallbacks
+def _processDefaultCalendarProperty(home, propname):
+    &quot;&quot;&quot;
+    Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><del>-        yield cleanPropertyStore()
</del><ins>+    inbox = (yield home.calendarWithName(&quot;inbox&quot;))
+    if inbox is not None:
+        prop = inbox.properties().get(PropertyName.fromElement(propname))
+        if prop is not None:
+            defaultCalendar = str(prop.children[0])
+            parts = defaultCalendar.split(&quot;/&quot;)
+            if len(parts) == 5:
</ins><span class="cx"> 
</span><del>-    except RuntimeError:
-        f = Failure()
-        yield sqlTxn.abort()
-        f.raiseException()
</del><ins>+                calendarName = parts[-1]
+                calendarHomeUID = parts[-2]
+                if calendarHomeUID == home.uid():
</ins><span class="cx"> 
</span><ins>+                    calendar = (yield home.calendarWithName(calendarName))
+                    if calendar is not None:
+                        try:
+                            if propname == caldavxml.ScheduleDefaultCalendarURL:
+                                ctype = &quot;VEVENT&quot;
+                            elif propname == customxml.ScheduleDefaultTasksURL:
+                                ctype = &quot;VTODO&quot;
+                            yield home.setDefaultCalendar(
+                                calendar, ctype
+                            )
+                        except InvalidDefaultCalendar:
+                            # Ignore these - the server will recover
+                            pass
</ins><span class="cx"> 
</span><ins>+            del inbox.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarTranspProperties(sqlStore):
</del><ins>+def moveCalendarTranspProperties(home):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Need to move all the CalDAV:schedule-calendar-transp properties in the
</span><span class="cx">     RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
</span><span class="cx">     the new value from the XML property.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    cb = schema.CALENDAR_BIND
-    rp = schema.RESOURCE_PROPERTY
</del><ins>+    # Iterate over each calendar (both owned and shared)
+    calendars = (yield home.loadChildren())
+    for calendar in calendars:
+        if calendar.isInbox():
+            prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+            if prop is not None:
+                del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
+        prop = calendar.properties().get(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp))
+        if prop is not None:
+            yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+            del calendar.properties()[PropertyName.fromElement(caldavxml.ScheduleCalendarTransp)]
</ins><span class="cx"> 
</span><del>-    try:
-        calendars_for_id = {}
-        while True:
-            sqlTxn = sqlStore.newTransaction()
-            rows = (yield rowsForProperty(sqlTxn, caldavxml.ScheduleCalendarTransp, with_uid=True, batch=BATCH_SIZE))
-            if len(rows) == 0:
-                yield sqlTxn.commit()
-                break
-            delete_ids = []
-            for calendar_rid, value, viewer in rows:
-                delete_ids.append(calendar_rid)
-                if calendar_rid not in calendars_for_id:
-                    ids = yield Select(
-                        [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
-                        From=cb,
-                        Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
-                    ).on(sqlTxn)
-                    calendars_for_id[calendar_rid] = ids
</del><span class="cx"> 
</span><del>-                if viewer:
-                    calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
-                else:
-                    calendarHome = None
-                    for row in calendars_for_id[calendar_rid]:
-                        home_id, bind_mode = row
-                        if bind_mode == _BIND_MODE_OWN:
-                            calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
-                            break
</del><span class="cx"> 
</span><del>-                if calendarHome is not None:
-                    prop = WebDAVDocument.fromString(value).root_element
-                    calendar = (yield calendarHome.childWithID(calendar_rid))
-                    if calendar is not None:
-                        yield calendar.setUsedForFreeBusy(prop == caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
-
-            # Always delete the rows so that batch processing works correctly
-            yield Delete(
-                From=rp,
-                Where=(rp.RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(delete_ids)))).And
-                      (rp.NAME == PropertyName.fromElement(caldavxml.ScheduleCalendarTransp).toString()),
-            ).on(sqlTxn, ids=delete_ids)
-
-            yield sqlTxn.commit()
-
-        sqlTxn = sqlStore.newTransaction()
-        yield removeProperty(sqlTxn, PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
-        yield sqlTxn.commit()
-        yield cleanPropertyStore()
-
-    except RuntimeError:
-        f = Failure()
-        yield sqlTxn.abort()
-        f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><del>-def moveDefaultAlarmProperties(sqlStore):
</del><ins>+def moveDefaultAlarmProperties(home):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Need to move all the CalDAV:default-calendar and CS:default-tasks properties in the
</span><span class="cx">     RESOURCE_PROPERTY table to the new CALENDAR_HOME_METADATA table columns, extracting
</span><span class="lines">@@ -209,25 +154,25 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     yield _processDefaultAlarmProperty(
</span><del>-        sqlStore,
</del><ins>+        home,
</ins><span class="cx">         caldavxml.DefaultAlarmVEventDateTime,
</span><span class="cx">         True,
</span><span class="cx">         True,
</span><span class="cx">     )
</span><span class="cx">     yield _processDefaultAlarmProperty(
</span><del>-        sqlStore,
</del><ins>+        home,
</ins><span class="cx">         caldavxml.DefaultAlarmVEventDate,
</span><span class="cx">         True,
</span><span class="cx">         False,
</span><span class="cx">     )
</span><span class="cx">     yield _processDefaultAlarmProperty(
</span><del>-        sqlStore,
</del><ins>+        home,
</ins><span class="cx">         caldavxml.DefaultAlarmVToDoDateTime,
</span><span class="cx">         False,
</span><span class="cx">         True,
</span><span class="cx">     )
</span><span class="cx">     yield _processDefaultAlarmProperty(
</span><del>-        sqlStore,
</del><ins>+        home,
</ins><span class="cx">         caldavxml.DefaultAlarmVToDoDate,
</span><span class="cx">         False,
</span><span class="cx">         False,
</span><span class="lines">@@ -236,90 +181,40 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def _processDefaultAlarmProperty(sqlStore, propname, vevent, timed):
</del><ins>+def _processDefaultAlarmProperty(home, propname, vevent, timed):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Move the specified property value to the matching CALENDAR_HOME_METADATA or CALENDAR_BIND table column.
</span><span class="cx"> 
</span><span class="cx">     Since the number of properties may well be large, we need to do this in batches.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    hm = schema.CALENDAR_HOME_METADATA
-    cb = schema.CALENDAR_BIND
-    rp = schema.RESOURCE_PROPERTY
</del><ins>+    # Check the home first
+    prop = home.properties().get(PropertyName.fromElement(propname))
+    if prop is not None:
+        alarm = str(prop.children[0]) if prop.children else None
+        yield home.setDefaultAlarm(alarm, vevent, timed)
+        del home.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx"> 
</span><del>-    try:
-        calendars_for_id = {}
-        while True:
-            sqlTxn = sqlStore.newTransaction()
-            rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
-            if len(rows) == 0:
-                yield sqlTxn.commit()
-                break
-            delete_ids = []
-            for rid, value, viewer in rows:
-                delete_ids.append(rid)
</del><ins>+    # Now each child
+    calendars = (yield home.loadChildren())
+    for calendar in calendars:
+        if calendar.isInbox():
+            continue
+        prop = calendar.properties().get(PropertyName.fromElement(propname))
+        if prop is not None:
+            alarm = str(prop.children[0]) if prop.children else None
+            yield calendar.setDefaultAlarm(alarm, vevent, timed)
+            del calendar.properties()[PropertyName.fromElement(propname)]
</ins><span class="cx"> 
</span><del>-                prop = WebDAVDocument.fromString(value).root_element
-                alarm = str(prop.children[0]) if prop.children else None
</del><span class="cx"> 
</span><del>-                # First check if the rid is a home - this is the most common case
-                ids = yield Select(
-                    [hm.RESOURCE_ID, ],
-                    From=hm,
-                    Where=hm.RESOURCE_ID == rid,
-                ).on(sqlTxn)
</del><span class="cx"> 
</span><del>-                if len(ids) &gt; 0:
-                    # Home object
-                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
-                    if calendarHome is not None:
-                        yield calendarHome.setDefaultAlarm(alarm, vevent, timed)
-                else:
-                    # rid is a calendar - we need to find the per-user calendar for the resource viewer
-                    if rid not in calendars_for_id:
-                        ids = yield Select(
-                            [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
-                            From=cb,
-                            Where=cb.CALENDAR_RESOURCE_ID == rid,
-                        ).on(sqlTxn)
-                        calendars_for_id[rid] = ids
-
-                    if viewer:
-                        calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
-                    else:
-                        calendarHome = None
-                        for row in calendars_for_id[rid]:
-                            home_id, bind_mode = row
-                            if bind_mode == _BIND_MODE_OWN:
-                                calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
-                                break
-
-                    if calendarHome is not None:
-                        calendar = yield calendarHome.childWithID(rid)
-                        if calendar is not None:
-                            yield calendar.setDefaultAlarm(alarm, vevent, timed)
-
-            # Always delete the rows so that batch processing works correctly
-            yield Delete(
-                From=rp,
-                Where=(rp.RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(delete_ids)))).And
-                      (rp.NAME == PropertyName.fromElement(propname).toString()),
-            ).on(sqlTxn, ids=delete_ids)
-
-            yield sqlTxn.commit()
-
-        yield cleanPropertyStore()
-
-    except RuntimeError:
-        f = Failure()
-        yield sqlTxn.abort()
-        f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeResourceType(sqlStore):
</span><ins>+    logUpgradeStatus(&quot;Starting Calendar Remove Resource Type&quot;)
+
</ins><span class="cx">     sqlTxn = sqlStore.newTransaction()
</span><span class="cx">     yield removeProperty(sqlTxn, PropertyName.fromElement(element.ResourceType))
</span><span class="cx">     yield sqlTxn.commit()
</span><span class="cx">     yield cleanPropertyStore()
</span><ins>+
+    logUpgradeStatus(&quot;End Calendar Remove Resource Type&quot;)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -15,21 +15,18 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from twext.enterprise.dal.syntax import Select, Delete, Parameter
</del><ins>+from twext.web2.dav.resource import TwistedQuotaUsedProperty, TwistedGETContentMD5
</ins><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><del>-from twisted.python.failure import Failure
</del><span class="cx"> 
</span><span class="cx"> from twistedcaldav import caldavxml, customxml
</span><ins>+from twistedcaldav.config import config
</ins><span class="cx"> 
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><del>-from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
-from txdav.common.datastore.upgrade.sql.upgrades.util import rowsForProperty, updateCalendarDataVersion, \
-    updateAllCalendarHomeDataVersions, removeProperty, cleanPropertyStore
</del><ins>+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.upgrade.sql.upgrades.util import updateCalendarDataVersion, \
+    removeProperty, cleanPropertyStore, logUpgradeStatus, doToEachHomeNotAtVersion
</ins><span class="cx"> from txdav.xml import element
</span><del>-from txdav.xml.parser import WebDAVDocument
-from twext.web2.dav.resource import TwistedQuotaUsedProperty, \
-    TwistedGETContentMD5
</del><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Data upgrade from database version 4 to 5
</span><span class="lines">@@ -43,136 +40,76 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Do the required upgrade steps.
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    yield moveCalendarTimezoneProperties(sqlStore)
-    yield moveCalendarAvailabilityProperties(sqlStore)
-    yield removeOtherProperties(sqlStore)
</del><ins>+    yield updateCalendarHomes(sqlStore, config.UpgradeHomePrefix)
</ins><span class="cx"> 
</span><del>-    # Always bump the DB value
-    yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
-    yield updateAllCalendarHomeDataVersions(sqlStore, UPGRADE_TO_VERSION)
</del><ins>+    # Don't do remaining upgrade if we are only process a subset of the homes
+    if not config.UpgradeHomePrefix:
+        yield removeOtherProperties(sqlStore)
</ins><span class="cx"> 
</span><ins>+        # Always bump the DB value
+        yield updateCalendarDataVersion(sqlStore, UPGRADE_TO_VERSION)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarTimezoneProperties(sqlStore):
</del><ins>+def updateCalendarHomes(sqlStore, prefix=None):
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    Need to move all the CalDAV:calendar-timezone properties in the
-    RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
-    the new value from the XML property.
</del><ins>+    For each calendar home, update the associated properties on the home or its owned calendars.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    cb = schema.CALENDAR_BIND
-    rp = schema.RESOURCE_PROPERTY
</del><ins>+    yield doToEachHomeNotAtVersion(sqlStore, schema.CALENDAR_HOME, UPGRADE_TO_VERSION, updateCalendarHome, &quot;Update Calendar Home&quot;, filterOwnerUID=prefix)
</ins><span class="cx"> 
</span><del>-    try:
-        calendars_for_id = {}
-        while True:
-            sqlTxn = sqlStore.newTransaction()
-            rows = (yield rowsForProperty(sqlTxn, caldavxml.CalendarTimeZone, with_uid=True, batch=BATCH_SIZE))
-            if len(rows) == 0:
-                yield sqlTxn.commit()
-                break
-            delete_ids = []
-            for calendar_rid, value, viewer in rows:
-                delete_ids.append(calendar_rid)
-                if calendar_rid not in calendars_for_id:
-                    ids = yield Select(
-                        [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
-                        From=cb,
-                        Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
-                    ).on(sqlTxn)
-                    calendars_for_id[calendar_rid] = ids
</del><span class="cx"> 
</span><del>-                if viewer:
-                    calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
-                else:
-                    calendarHome = None
-                    for row in calendars_for_id[calendar_rid]:
-                        home_id, bind_mode = row
-                        if bind_mode == _BIND_MODE_OWN:
-                            calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
-                            break
</del><span class="cx"> 
</span><del>-                if calendarHome is not None:
-                    prop = WebDAVDocument.fromString(value).root_element
-                    calendar = (yield calendarHome.childWithID(calendar_rid))
-                    if calendar is not None:
-                        yield calendar.setTimezone(prop.calendar())
</del><ins>+@inlineCallbacks
+def updateCalendarHome(txn, homeResourceID):
+    &quot;&quot;&quot;
+    For this calendar home, update the associated properties on the home or its owned calendars.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><del>-            # Always delete the rows so that batch processing works correctly
-            yield Delete(
-                From=rp,
-                Where=(rp.RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(delete_ids)))).And
-                      (rp.NAME == PropertyName.fromElement(caldavxml.CalendarTimeZone).toString()),
-            ).on(sqlTxn, ids=delete_ids)
</del><ins>+    home = yield txn.calendarHomeWithResourceID(homeResourceID)
+    yield moveCalendarTimezoneProperties(home)
+    yield moveCalendarAvailabilityProperties(home)
+    yield cleanPropertyStore()
</ins><span class="cx"> 
</span><del>-            yield sqlTxn.commit()
</del><span class="cx"> 
</span><del>-        yield cleanPropertyStore()
</del><span class="cx"> 
</span><del>-    except RuntimeError:
-        f = Failure()
-        yield sqlTxn.abort()
-        f.raiseException()
</del><ins>+@inlineCallbacks
+def moveCalendarTimezoneProperties(home):
+    &quot;&quot;&quot;
+    Need to move all the CalDAV:calendar-timezone properties in the
+    RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
+    the new value from the XML property.
+    &quot;&quot;&quot;
</ins><span class="cx"> 
</span><ins>+    # Iterate over each calendar (both owned and shared)
+    calendars = (yield home.loadChildren())
+    for calendar in calendars:
+        if calendar.isInbox():
+            continue
+        prop = calendar.properties().get(PropertyName.fromElement(caldavxml.CalendarTimeZone))
+        if prop is not None:
+            yield calendar.setTimezone(prop.calendar())
+            del calendar.properties()[PropertyName.fromElement(caldavxml.CalendarTimeZone)]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><del>-def moveCalendarAvailabilityProperties(sqlStore):
</del><ins>+def moveCalendarAvailabilityProperties(home):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Need to move all the CS:calendar-availability properties in the
</span><span class="cx">     RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
</span><span class="cx">     the new value from the XML property.
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+    inbox = (yield home.calendarWithName(&quot;inbox&quot;))
+    if inbox is not None:
+        prop = inbox.properties().get(PropertyName.fromElement(customxml.CalendarAvailability))
+        if prop is not None:
+            yield home.setAvailability(prop.calendar())
+            del inbox.properties()[PropertyName.fromElement(customxml.CalendarAvailability)]
</ins><span class="cx"> 
</span><del>-    cb = schema.CALENDAR_BIND
-    rp = schema.RESOURCE_PROPERTY
</del><span class="cx"> 
</span><del>-    try:
-        while True:
-            sqlTxn = sqlStore.newTransaction()
-            rows = (yield rowsForProperty(sqlTxn, customxml.CalendarAvailability, batch=BATCH_SIZE))
-            if len(rows) == 0:
-                yield sqlTxn.commit()
-                break
</del><span class="cx"> 
</span><del>-            # Map each calendar to a home id using a single query for efficiency
-            calendar_ids = [row[0] for row in rows]
-
-            home_map = yield Select(
-                [cb.CALENDAR_RESOURCE_ID, cb.CALENDAR_HOME_RESOURCE_ID, ],
-                From=cb,
-                Where=(cb.CALENDAR_RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(calendar_ids)))).And(cb.BIND_MODE == _BIND_MODE_OWN),
-            ).on(sqlTxn, ids=calendar_ids)
-            calendar_to_home = dict(home_map)
-
-            # Move property to each home
-            for calendar_rid, value in rows:
-                if calendar_rid in calendar_to_home:
-                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(calendar_to_home[calendar_rid]))
-
-                    if calendarHome is not None:
-                        prop = WebDAVDocument.fromString(value).root_element
-                        yield calendarHome.setAvailability(prop.calendar())
-
-            # Always delete the rows so that batch processing works correctly
-            yield Delete(
-                From=rp,
-                Where=(rp.RESOURCE_ID.In(Parameter(&quot;ids&quot;, len(calendar_ids)))).And
-                      (rp.NAME == PropertyName.fromElement(customxml.CalendarAvailability).toString()),
-            ).on(sqlTxn, ids=calendar_ids)
-
-            yield sqlTxn.commit()
-
-        yield cleanPropertyStore()
-
-    except RuntimeError:
-        f = Failure()
-        yield sqlTxn.abort()
-        f.raiseException()
-
-
-
</del><span class="cx"> @inlineCallbacks
</span><span class="cx"> def removeOtherProperties(sqlStore):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -190,6 +127,8 @@
</span><span class="cx">     {http://twistedmatrix.com/xml_namespace/dav/}schedule-auto-respond
</span><span class="cx"> 
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+    logUpgradeStatus(&quot;Starting Calendar Remove Other Properties&quot;)
+
</ins><span class="cx">     sqlTxn = sqlStore.newTransaction()
</span><span class="cx"> 
</span><span class="cx">     yield removeProperty(sqlTxn, PropertyName.fromElement(element.ACL))
</span><span class="lines">@@ -205,3 +144,5 @@
</span><span class="cx"> 
</span><span class="cx">     yield sqlTxn.commit()
</span><span class="cx">     yield cleanPropertyStore()
</span><ins>+
+    logUpgradeStatus(&quot;End Calendar Remove Other Properties&quot;)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -13,23 +13,27 @@
</span><span class="cx"> # See the License for the specific language governing permissions and
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><ins>+
+from twext.enterprise.dal.syntax import Update, Insert
+
+from twistedcaldav import caldavxml
</ins><span class="cx"> from twistedcaldav.caldavxml import ScheduleDefaultCalendarURL, \
</span><del>-    CalendarFreeBusySet, Opaque, ScheduleCalendarTransp
</del><ins>+    CalendarFreeBusySet, Opaque, ScheduleCalendarTransp, Transparent
+
</ins><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.datastore.test.util import CommonStoreTests
</span><ins>+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
+from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_3_to_4 import updateCalendarHomes, \
+    doUpgrade
+from txdav.xml import element
</ins><span class="cx"> from txdav.xml.element import HRef
</span><del>-from twext.enterprise.dal.syntax import Update, Insert
-from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_3_to_4 import moveDefaultCalendarProperties, \
-    moveCalendarTranspProperties, removeResourceType, moveDefaultAlarmProperties
-from txdav.xml import element
-from twistedcaldav import caldavxml
-from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
</del><ins>+from twistedcaldav.config import config
</ins><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+from twisted.internet.defer import inlineCallbacks, returnValue
</ins><span class="cx"> 
</span><span class="cx"> class Upgrade_from_3_to_4(CommonStoreTests):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -37,7 +41,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_defaultCalendarUpgrade(self):
</del><ins>+    def _defaultCalendarUpgrade_setup(self):
</ins><span class="cx"> 
</span><span class="cx">         # Set dead property on inbox
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="lines">@@ -52,39 +56,132 @@
</span><span class="cx">                 Where=chm.RESOURCE_ID == home._resourceID,
</span><span class="cx">             ).on(self.transactionUnderTest())
</span><span class="cx"> 
</span><del>-        # Force data version to previous
-        ch = home._homeSchema
-        yield Update(
-            {ch.DATAVERSION: 3},
-            Where=ch.RESOURCE_ID == home._resourceID,
-        ).on(self.transactionUnderTest())
</del><ins>+            # Force data version to previous
+            ch = home._homeSchema
+            yield Update(
+                {ch.DATAVERSION: 3},
+                Where=ch.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
</ins><span class="cx"> 
</span><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield moveDefaultCalendarProperties(self._sqlCalendarStore)
</del><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def _defaultCalendarUpgrade_check(self, changed_users, unchanged_users):
+
</ins><span class="cx">         # Test results
</span><del>-        for user in (&quot;user01&quot;, &quot;user02&quot;,):
</del><ins>+        for user in changed_users:
</ins><span class="cx">             home = (yield self.homeUnderTest(name=user))
</span><ins>+            version = (yield home.dataVersion())
+            self.assertEqual(version, 4)
</ins><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="cx">             self.assertTrue(home.isDefaultCalendar(calendar))
</span><span class="cx">             inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
</span><span class="cx">             self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
</span><span class="cx"> 
</span><ins>+        for user in unchanged_users:
+            home = (yield self.homeUnderTest(name=user))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 3)
+            calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+            self.assertFalse(home.isDefaultCalendar(calendar))
+            inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+            self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) in inbox.properties())
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><del>-    def test_calendarTranspUpgrade(self):
</del><ins>+    def test_defaultCalendarUpgrade(self):
+        yield self._defaultCalendarUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def test_partialDefaultCalendarUpgrade(self):
+        yield self._defaultCalendarUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+
+
+    @inlineCallbacks
+    def _invalidDefaultCalendarUpgrade_setup(self):
+
</ins><span class="cx">         # Set dead property on inbox
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="cx">             inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
</span><ins>+            inbox.properties()[PropertyName.fromElement(ScheduleDefaultCalendarURL)] = ScheduleDefaultCalendarURL(HRef.fromString(&quot;/calendars/__uids__/%s/tasks_1&quot; % (user,)))
+
+            # Force current default to null
+            home = (yield self.homeUnderTest(name=user))
+            chm = home._homeMetaDataSchema
+            yield Update(
+                {chm.DEFAULT_EVENTS: None},
+                Where=chm.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
+
+            # Create tasks only calendar
+            tasks = (yield home.createCalendarWithName(&quot;tasks_1&quot;))
+            yield tasks.setSupportedComponents(&quot;VTODO&quot;)
+
+            # Force data version to previous
+            ch = home._homeSchema
+            yield Update(
+                {ch.DATAVERSION: 3},
+                Where=ch.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
+
+        yield self.commit()
+
+
+    @inlineCallbacks
+    def _invalidDefaultCalendarUpgrade_check(self, changed_users, unchanged_users):
+
+        # Test results
+        for user in changed_users:
+            home = (yield self.homeUnderTest(name=user))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 4)
+            calendar = (yield self.calendarUnderTest(name=&quot;tasks_1&quot;, home=user))
+            self.assertFalse(home.isDefaultCalendar(calendar))
+            inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+            self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) not in inbox.properties())
+
+        for user in unchanged_users:
+            home = (yield self.homeUnderTest(name=user))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 3)
+            calendar = (yield self.calendarUnderTest(name=&quot;tasks_1&quot;, home=user))
+            self.assertFalse(home.isDefaultCalendar(calendar))
+            inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+            self.assertTrue(PropertyName.fromElement(ScheduleDefaultCalendarURL) in inbox.properties())
+
+
+    @inlineCallbacks
+    def test_invalidDefaultCalendarUpgrade(self):
+        yield self._invalidDefaultCalendarUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._invalidDefaultCalendarUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
+
+
+    @inlineCallbacks
+    def test_partialInvalidDefaultCalendarUpgrade(self):
+        yield self._invalidDefaultCalendarUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._invalidDefaultCalendarUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+
+
+    @inlineCallbacks
+    def _calendarTranspUpgrade_setup(self):
+
+        # Set dead property on inbox
+        for user in (&quot;user01&quot;, &quot;user02&quot;,):
+            inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
</ins><span class="cx">             inbox.properties()[PropertyName.fromElement(CalendarFreeBusySet)] = CalendarFreeBusySet(HRef.fromString(&quot;/calendars/__uids__/%s/calendar_1&quot; % (user,)))
</span><span class="cx"> 
</span><span class="cx">             # Force current to transparent
</span><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="cx">             yield calendar.setUsedForFreeBusy(False)
</span><del>-            calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque())
</del><ins>+            calendar.properties()[PropertyName.fromElement(ScheduleCalendarTransp)] = ScheduleCalendarTransp(Opaque() if user == &quot;user01&quot; else Transparent())
</ins><span class="cx"> 
</span><span class="cx">             # Force data version to previous
</span><span class="cx">             home = (yield self.homeUnderTest(name=user))
</span><span class="lines">@@ -118,21 +215,55 @@
</span><span class="cx">         ).on(txn)
</span><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield moveCalendarTranspProperties(self._sqlCalendarStore)
</del><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def _calendarTranspUpgrade_check(self, changed_users, unchanged_users):
+
</ins><span class="cx">         # Test results
</span><del>-        for user in (&quot;user01&quot;, &quot;user02&quot;,):
</del><ins>+        for user in changed_users:
</ins><span class="cx">             home = (yield self.homeUnderTest(name=user))
</span><ins>+            version = (yield home.dataVersion())
+            self.assertEqual(version, 4)
</ins><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><del>-            self.assertTrue(calendar.isUsedForFreeBusy())
</del><ins>+            if user == &quot;user01&quot;:
+                self.assertTrue(calendar.isUsedForFreeBusy())
+            else:
+                self.assertFalse(calendar.isUsedForFreeBusy())
+            self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) not in calendar.properties())
</ins><span class="cx">             inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
</span><span class="cx">             self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) not in inbox.properties())
</span><span class="cx"> 
</span><ins>+        for user in unchanged_users:
+            home = (yield self.homeUnderTest(name=user))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 3)
+            calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+            if user == &quot;user01&quot;:
+                self.assertFalse(calendar.isUsedForFreeBusy())
+            else:
+                self.assertFalse(calendar.isUsedForFreeBusy())
+            self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) in calendar.properties())
+            inbox = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+            self.assertTrue(PropertyName.fromElement(CalendarFreeBusySet) in inbox.properties())
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @inlineCallbacks
</span><del>-    def test_defaultAlarmUpgrade(self):
</del><ins>+    def test_calendarTranspUpgrade(self):
+        yield self._calendarTranspUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def test_partialCalendarTranspUpgrade(self):
+        yield self._calendarTranspUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+
+
+    @inlineCallbacks
+    def _defaultAlarmUpgrade_setup(self):
+
</ins><span class="cx">         alarmhome1 = &quot;&quot;&quot;BEGIN:VALARM
</span><span class="cx"> ACTION:AUDIO
</span><span class="cx"> TRIGGER;RELATED=START:-PT1M
</span><span class="lines">@@ -236,13 +367,28 @@
</span><span class="cx">         shared = yield self.calendarUnderTest(name=shared_name, home=&quot;user02&quot;)
</span><span class="cx">         for _ignore_vevent, _ignore_timed, alarm, prop in detailsshared:
</span><span class="cx">             shared.properties()[PropertyName.fromElement(prop)] = prop(alarm)
</span><ins>+
+        for user in (&quot;user01&quot;, &quot;user02&quot;,):
+            # Force data version to previous
+            home = (yield self.homeUnderTest(name=user))
+            ch = home._homeSchema
+            yield Update(
+                {ch.DATAVERSION: 3},
+                Where=ch.RESOURCE_ID == home._resourceID,
+            ).on(self.transactionUnderTest())
+
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield moveDefaultAlarmProperties(self._sqlCalendarStore)
</del><ins>+        returnValue((detailshome, detailscalendar, detailsshared, shared_name,))
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def _defaultAlarmUpgrade_check(self, changed_users, unchanged_users, detailshome, detailscalendar, detailsshared, shared_name):
+
</ins><span class="cx">         # Check each type of collection
</span><span class="cx">         home = yield self.homeUnderTest(name=&quot;user01&quot;)
</span><ins>+        version = (yield home.dataVersion())
+        self.assertEqual(version, 4)
</ins><span class="cx">         for vevent, timed, alarm, prop in detailshome:
</span><span class="cx">             alarm_result = (yield home.getDefaultAlarm(vevent, timed))
</span><span class="cx">             self.assertEquals(alarm_result, alarm)
</span><span class="lines">@@ -252,18 +398,67 @@
</span><span class="cx">         for vevent, timed, alarm, prop in detailscalendar:
</span><span class="cx">             alarm_result = (yield calendar.getDefaultAlarm(vevent, timed))
</span><span class="cx">             self.assertEquals(alarm_result, alarm)
</span><del>-            self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
</del><ins>+            self.assertTrue(PropertyName.fromElement(prop) not in calendar.properties())
</ins><span class="cx"> 
</span><del>-        shared = yield self.calendarUnderTest(name=shared_name, home=&quot;user02&quot;)
-        for vevent, timed, alarm, prop in detailsshared:
-            alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
-            self.assertEquals(alarm_result, alarm)
-            self.assertTrue(PropertyName.fromElement(prop) not in home.properties())
</del><ins>+        if &quot;user02&quot; in changed_users:
+            home = (yield self.homeUnderTest(name=&quot;user02&quot;))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 4)
+            shared = yield self.calendarUnderTest(name=shared_name, home=&quot;user02&quot;)
+            for vevent, timed, alarm, prop in detailsshared:
+                alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
+                self.assertEquals(alarm_result, alarm)
+                self.assertTrue(PropertyName.fromElement(prop) not in shared.properties())
+        else:
+            home = (yield self.homeUnderTest(name=&quot;user02&quot;))
+            version = (yield home.dataVersion())
+            self.assertEqual(version, 3)
+            shared = yield self.calendarUnderTest(name=shared_name, home=&quot;user02&quot;)
+            for vevent, timed, alarm, prop in detailsshared:
+                alarm_result = (yield shared.getDefaultAlarm(vevent, timed))
+                self.assertEquals(alarm_result, None)
+                self.assertTrue(PropertyName.fromElement(prop) in shared.properties())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_resourceTypeUpgrade(self):
</del><ins>+    def test_defaultAlarmUpgrade(self):
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), (), detailshome, detailscalendar, detailsshared, shared_name)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def test_partialDefaultAlarmUpgrade(self):
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+    @inlineCallbacks
+    def test_combinedUpgrade(self):
+        yield self._defaultCalendarUpgrade_setup()
+        yield self._calendarTranspUpgrade_setup()
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), (), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+    @inlineCallbacks
+    def test_partialCombinedUpgrade(self):
+        yield self._defaultCalendarUpgrade_setup()
+        yield self._calendarTranspUpgrade_setup()
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,), detailshome, detailscalendar, detailsshared, shared_name)
+
+
+    @inlineCallbacks
+    def _resourceTypeUpgrade_setup(self):
+
</ins><span class="cx">         # Set dead property on calendar
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="lines">@@ -273,12 +468,60 @@
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="cx">             self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
</span><ins>+
+        yield self.transactionUnderTest().updateCalendarserverValue(&quot;CALENDAR-DATAVERSION&quot;, &quot;3&quot;)
+
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield removeResourceType(self._sqlCalendarStore)
</del><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def _resourceTypeUpgrade_check(self, full=True):
+
</ins><span class="cx">         # Test results
</span><del>-        for user in (&quot;user01&quot;, &quot;user02&quot;,):
-            calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
-            self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
</del><ins>+        if full:
+            for user in (&quot;user01&quot;, &quot;user02&quot;,):
+                calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+                self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
+            version = yield self.transactionUnderTest().calendarserverValue(&quot;CALENDAR-DATAVERSION&quot;)
+            self.assertEqual(int(version), 4)
+        else:
+            for user in (&quot;user01&quot;, &quot;user02&quot;,):
+                calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+                self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
+            version = yield self.transactionUnderTest().calendarserverValue(&quot;CALENDAR-DATAVERSION&quot;)
+            self.assertEqual(int(version), 3)
+
+
+    @inlineCallbacks
+    def test_resourceTypeUpgrade(self):
+        yield self._resourceTypeUpgrade_setup()
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._resourceTypeUpgrade_check()
+
+
+    @inlineCallbacks
+    def test_fullUpgrade(self):
+        self.patch(config, &quot;UpgradeHomePrefix&quot;, &quot;&quot;)
+        yield self._defaultCalendarUpgrade_setup()
+        yield self._calendarTranspUpgrade_setup()
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield self._resourceTypeUpgrade_setup()
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), ())
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;, &quot;user02&quot;,), (), detailshome, detailscalendar, detailsshared, shared_name)
+        yield self._resourceTypeUpgrade_check()
+
+
+    @inlineCallbacks
+    def test_partialFullUpgrade(self):
+        self.patch(config, &quot;UpgradeHomePrefix&quot;, &quot;user01&quot;)
+        yield self._defaultCalendarUpgrade_setup()
+        yield self._calendarTranspUpgrade_setup()
+        yield self._resourceTypeUpgrade_setup()
+        detailshome, detailscalendar, detailsshared, shared_name = (yield self._defaultAlarmUpgrade_setup())
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._defaultCalendarUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+        yield self._calendarTranspUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,))
+        yield self._defaultAlarmUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;,), detailshome, detailscalendar, detailsshared, shared_name)
+        yield self._resourceTypeUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -13,21 +13,24 @@
</span><span class="cx"> # See the License for the specific language governing permissions and
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><del>-from twistedcaldav import caldavxml, customxml
-from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_4_to_5 import moveCalendarTimezoneProperties, \
-    removeOtherProperties, moveCalendarAvailabilityProperties
-from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
-from txdav.xml import element
</del><span class="cx"> 
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> from twext.enterprise.dal.syntax import Update, Insert
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+from twistedcaldav import caldavxml, customxml
+from twistedcaldav.config import config
</ins><span class="cx"> from twistedcaldav.ical import Component
</span><ins>+
</ins><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="cx"> from txdav.caldav.datastore.test.util import CommonStoreTests
</span><ins>+from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE, schema
+from txdav.common.datastore.upgrade.sql.upgrades.calendar_upgrade_from_4_to_5 import updateCalendarHomes, doUpgrade
+from txdav.xml import element
</ins><span class="cx"> 
</span><span class="cx"> class Upgrade_from_4_to_5(CommonStoreTests):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="lines">@@ -35,7 +38,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_calendarTimezoneUpgrade(self):
</del><ins>+    def _calendarTimezoneUpgrade_setup(self):
</ins><span class="cx"> 
</span><span class="cx">         tz1 = Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="lines">@@ -137,19 +140,50 @@
</span><span class="cx">         ).on(txn)
</span><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield moveCalendarTimezoneProperties(self._sqlCalendarStore)
</del><ins>+        returnValue(user_details)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def _calendarTimezoneUpgrade_check(self, changed_users, unchanged_users, user_details):
+
</ins><span class="cx">         # Test results
</span><span class="cx">         for user, calname, tz in user_details:
</span><del>-            calendar = (yield self.calendarUnderTest(name=calname, home=user))
-            self.assertEqual(calendar.getTimezone(), tz)
-            self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
</del><ins>+            if user in changed_users:
+                home = (yield self.homeUnderTest(name=user))
+                version = (yield home.dataVersion())
+                self.assertEqual(version, 5)
+                calendar = (yield self.calendarUnderTest(name=calname, home=user))
+                self.assertEqual(calendar.getTimezone(), tz)
+                self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
+            else:
+                home = (yield self.homeUnderTest(name=user))
+                version = (yield home.dataVersion())
+                self.assertEqual(version, 4)
+                calendar = (yield self.calendarUnderTest(name=calname, home=user))
+                self.assertEqual(calendar.getTimezone(), None)
+                if tz:
+                    self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) in calendar.properties())
+                else:
+                    self.assertTrue(PropertyName.fromElement(caldavxml.CalendarTimeZone) not in calendar.properties())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_calendarAvailabilityUpgrade(self):
</del><ins>+    def test_calendarTimezoneUpgrade(self):
+        user_details = yield self._calendarTimezoneUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def test_partialCalendarTimezoneUpgrade(self):
+        user_details = yield self._calendarTimezoneUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details)
+
+
+    @inlineCallbacks
+    def _calendarAvailabilityUpgrade_setup(self):
+
</ins><span class="cx">         av1 = Component.fromString(&quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span><span class="cx"> CALSCALE:GREGORIAN
</span><span class="lines">@@ -220,20 +254,68 @@
</span><span class="cx">             self.assertEqual(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties(), av is not None)
</span><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield moveCalendarAvailabilityProperties(self._sqlCalendarStore)
</del><ins>+        returnValue(user_details)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def _calendarAvailabilityUpgrade_check(self, changed_users, unchanged_users, user_details):
+
</ins><span class="cx">         # Test results
</span><span class="cx">         for user, av in user_details:
</span><del>-            home = (yield self.homeUnderTest(name=user))
-            calendar = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
-            self.assertEqual(home.getAvailability(), av)
-            self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
</del><ins>+            if user in changed_users:
+                home = (yield self.homeUnderTest(name=user))
+                version = (yield home.dataVersion())
+                self.assertEqual(version, 5)
+                calendar = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+                self.assertEqual(home.getAvailability(), av)
+                self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
+            else:
+                home = (yield self.homeUnderTest(name=user))
+                version = (yield home.dataVersion())
+                self.assertEqual(version, 4)
+                calendar = (yield self.calendarUnderTest(name=&quot;inbox&quot;, home=user))
+                self.assertEqual(home.getAvailability(), None)
+                if av:
+                    self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) in calendar.properties())
+                else:
+                    self.assertTrue(PropertyName.fromElement(customxml.CalendarAvailability) not in calendar.properties())
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_removeOtherPropertiesUpgrade(self):
</del><ins>+    def test_calendarAvailabilityUpgrade(self):
+        user_details = yield self._calendarAvailabilityUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
+    def test_partialCalendarAvailabilityUpgrade(self):
+        user_details = yield self._calendarAvailabilityUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details)
+
+
+    @inlineCallbacks
+    def test_combinedUpgrade(self):
+        user_details1 = yield self._calendarTimezoneUpgrade_setup()
+        user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details1)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details2)
+
+
+    @inlineCallbacks
+    def test_partialCombinedUpgrade(self):
+        user_details1 = yield self._calendarTimezoneUpgrade_setup()
+        user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+        yield updateCalendarHomes(self._sqlCalendarStore, &quot;user01&quot;)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details1)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details2)
+
+
+    @inlineCallbacks
+    def _removeOtherPropertiesUpgrade_setup(self):
+
</ins><span class="cx">         # Set dead property on calendar
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="lines">@@ -243,12 +325,55 @@
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><span class="cx">             calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
</span><span class="cx">             self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
</span><ins>+
+        yield self.transactionUnderTest().updateCalendarserverValue(&quot;CALENDAR-DATAVERSION&quot;, &quot;4&quot;)
+
</ins><span class="cx">         yield self.commit()
</span><span class="cx"> 
</span><del>-        # Trigger upgrade
-        yield removeOtherProperties(self._sqlCalendarStore)
</del><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def _removeOtherPropertiesUpgrade_check(self, full=True):
+
</ins><span class="cx">         # Test results
</span><span class="cx">         for user in (&quot;user01&quot;, &quot;user02&quot;,):
</span><del>-            calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
-            self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
</del><ins>+            if full:
+                calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+                self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
+                version = yield self.transactionUnderTest().calendarserverValue(&quot;CALENDAR-DATAVERSION&quot;)
+                self.assertEqual(int(version), 5)
+            else:
+                calendar = (yield self.calendarUnderTest(name=&quot;calendar_1&quot;, home=user))
+                self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
+                version = yield self.transactionUnderTest().calendarserverValue(&quot;CALENDAR-DATAVERSION&quot;)
+                self.assertEqual(int(version), 4)
+
+
+    @inlineCallbacks
+    def test_removeOtherPropertiesUpgrade(self):
+        yield self._removeOtherPropertiesUpgrade_setup()
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._removeOtherPropertiesUpgrade_check()
+
+
+    @inlineCallbacks
+    def test_fullUpgrade(self):
+        self.patch(config, &quot;UpgradeHomePrefix&quot;, &quot;&quot;)
+        user_details1 = yield self._calendarTimezoneUpgrade_setup()
+        user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+        yield self._removeOtherPropertiesUpgrade_setup()
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details1)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;, &quot;user02&quot;, &quot;user03&quot;,), (), user_details2)
+        yield self._removeOtherPropertiesUpgrade_check()
+
+
+    @inlineCallbacks
+    def test_partialFullUpgrade(self):
+        self.patch(config, &quot;UpgradeHomePrefix&quot;, &quot;user01&quot;)
+        user_details1 = yield self._calendarTimezoneUpgrade_setup()
+        user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+        yield self._removeOtherPropertiesUpgrade_setup()
+        yield doUpgrade(self._sqlCalendarStore)
+        yield self._calendarTimezoneUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details1)
+        yield self._calendarAvailabilityUpgrade_check((&quot;user01&quot;,), (&quot;user02&quot;, &quot;user03&quot;,), user_details2)
+        yield self._removeOtherPropertiesUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboojsontxdavcommondatastoreupgradesqlupgradesutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/util.py (11911 => 11912)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/util.py        2013-11-08 02:59:43 UTC (rev 11911)
+++ CalendarServer/branches/users/cdaboo/json/txdav/common/datastore/upgrade/sql/upgrades/util.py        2013-11-08 03:33:05 UTC (rev 11912)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx"> 
</span><del>-from twext.enterprise.dal.syntax import Select, Delete, Update
</del><ins>+from twext.enterprise.dal.syntax import Select, Delete, Update, Count
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from txdav.base.propertystore.base import PropertyName
</span><span class="lines">@@ -44,6 +44,21 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><ins>+def countProperty(txn, propelement):
+    pname = PropertyName.fromElement(propelement)
+
+    rp = schema.RESOURCE_PROPERTY
+    count = (yield Select(
+        [Count(rp.RESOURCE_ID), ],
+        From=rp,
+        Where=rp.NAME == pname.toString(),
+    ).on(txn))[0][0]
+
+    returnValue(count)
+
+
+
+@inlineCallbacks
</ins><span class="cx"> def cleanPropertyStore():
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     We have manually manipulated the SQL property store by-passing the underlying implementation's caching
</span><span class="lines">@@ -114,27 +129,43 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def doToEachHomeNotAtVersion(store, homeSchema, version, doIt):
</del><ins>+def doToEachHomeNotAtVersion(store, homeSchema, version, doIt, logStr, filterOwnerUID=None):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Do something to each home whose version column indicates it is older
</span><del>-    than the specified version. Do this in batches as there may be a lot of work to do.
</del><ins>+    than the specified version. Do this in batches as there may be a lot of work to do. Also,
+    allow the GUID to be filtered to support a parallel mode of operation.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    txn = store.newTransaction(&quot;updateDataVersion&quot;)
+    where = homeSchema.DATAVERSION &lt; version
+    if filterOwnerUID:
+        where = where.And(homeSchema.OWNER_UID.StartsWith(filterOwnerUID))
+    total = (yield Select(
+        [Count(homeSchema.RESOURCE_ID), ],
+        From=homeSchema,
+        Where=where,
+    ).on(txn))[0][0]
+    yield txn.commit()
+    count = 0
+
</ins><span class="cx">     while True:
</span><span class="cx"> 
</span><ins>+        logUpgradeStatus(logStr, count, total)
+
</ins><span class="cx">         # Get the next home with an old version
</span><span class="cx">         txn = store.newTransaction(&quot;updateDataVersion&quot;)
</span><span class="cx">         try:
</span><span class="cx">             rows = yield Select(
</span><span class="cx">                 [homeSchema.RESOURCE_ID, homeSchema.OWNER_UID, ],
</span><span class="cx">                 From=homeSchema,
</span><del>-                Where=homeSchema.DATAVERSION &lt; version,
</del><ins>+                Where=where,
</ins><span class="cx">                 OrderBy=homeSchema.OWNER_UID,
</span><span class="cx">                 Limit=1,
</span><span class="cx">             ).on(txn)
</span><span class="cx"> 
</span><span class="cx">             if len(rows) == 0:
</span><span class="cx">                 yield txn.commit()
</span><ins>+                logUpgradeStatus(&quot;End {}&quot;.format(logStr), count, total)
</ins><span class="cx">                 returnValue(None)
</span><span class="cx"> 
</span><span class="cx">             # Apply to the home
</span><span class="lines">@@ -149,6 +180,26 @@
</span><span class="cx">             yield txn.commit()
</span><span class="cx">         except RuntimeError, e:
</span><span class="cx">             f = Failure()
</span><del>-            log.error(&quot;Failed to upgrade %s to %s: %s&quot; % (homeSchema, version, e))
</del><ins>+            logUpgradeError(
+                logStr,
+                &quot;Failed to upgrade {} to {}: {}&quot;.format(homeSchema, version, e)
+            )
</ins><span class="cx">             yield txn.abort()
</span><span class="cx">             f.raiseException()
</span><ins>+
+        count += 1
+
+
+
+def logUpgradeStatus(title, count=None, total=None):
+    if total is None:
+        log.info(&quot;Database upgrade {title}&quot;, title=title)
+    else:
+        divisor = 1000 if total &gt; 1000 else 100
+        if (divmod(count, divisor)[1] == 0) or (count == total):
+            log.info(&quot;Database upgrade {title}: {count} of {total}&quot;, title=title, count=count, total=total)
+
+
+
+def logUpgradeError(title, details):
+    log.error(&quot;Database upgrade {title} failed: {details}&quot;, title=title, details=details)
</ins></span></pre>
</div>
</div>

</body>
</html>