<!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>[11871] CalendarServer/branches/users/cdaboo/fix-no-ischedule</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/11871">11871</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-11-01 15:25:30 -0700 (Fri, 01 Nov 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Merged from trunk.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarserveraccesslogpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/accesslog.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarserverprovisionrootpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/provision/root.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushamppushpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/amppush.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushnotifierpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/notifier.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushtesttest_notifierpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/test/test_notifier.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertapcaldavpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertaputilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsagentpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/agent.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsdbinspectpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/dbinspect.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsgatewaypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/gateway.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolstesttest_agentpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/test/test_agent.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsupgradepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduleconfauthaccountstestxml">CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/auth/accounts-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduleconfcaldavdappleplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/caldavd-apple.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestconfigdistplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.dist.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestconfigplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestpopulationpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/population.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestsimpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/sim.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtesttest_simpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/test_sim.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformancesqlusagerequestshttpTestspy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/requests/httpTests.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformancesqlusagesqlusagepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/sqlusage.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribtoolsfix_calendar">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/fix_calendar</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribtoolsprotocolanalysispy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/protocolanalysis.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulesupportbuildsh">CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/build.sh</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulesupportversionpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/version.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletestserver">CalendarServer/branches/users/cdaboo/fix-no-ischedule/testserver</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextenterpriseadbapi2py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/adbapi2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisedalsyntaxpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/syntax.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisedaltesttest_sqlsyntaxpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/test/test_sqlsyntax.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextenterpriseienterprisepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/ienterprise.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisetesttest_adbapi2py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/test/test_adbapi2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextinternetsendfdportpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/sendfdport.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextinternettesttest_sendfdportpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/test/test_sendfdport.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextpatchespy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/patches.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextpythonlogpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/log.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextpythontesttest_logpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/test/test_log.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2channelhttppy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/channel/http.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2davtesttest_utilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2davutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2metafdpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/metafd.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2testtest_httppy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_http.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextweb2testtest_metafdpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_metafd.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoaggregatepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/aggregate.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhodirectorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoexpressionpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/expression.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoidirectorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/idirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoindexpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/index.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwextwhoxmlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/xml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavcaldavxmlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/caldavxml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectoryappleopendirectorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/appleopendirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorydirectorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectoryldapdirectorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/ldapdirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorytesttest_buildquerypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_buildquery.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorytesttest_directorypy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavmethodreport_sync_collectionpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/method/report_sync_collection.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavresourcepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavscheduling_storecaldavresourcepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavstdconfigpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavstorebridgepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAfricaJubaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Africa/Juba.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaAnguillaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Anguilla.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaAraguainaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Araguaina.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaArgentinaSan_Luisics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Argentina/San_Luis.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaArubaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Aruba.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaCaymanics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Cayman.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaDominicaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Dominica.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGrand_Turkics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grand_Turk.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGrenadaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grenada.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGuadeloupeics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Guadeloupe.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaJamaicaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Jamaica.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaMarigotics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Marigot.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaMontserratics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Montserrat.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Barthelemyics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Barthelemy.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Kittsics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Kitts.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Luciaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Lucia.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Thomasics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Thomas.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Vincentics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Vincent.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaTortolaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Tortola.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaVirginics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Virgin.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAntarcticaMcMurdoics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/McMurdo.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAntarcticaSouth_Poleics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/South_Pole.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaAmmanics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Amman.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaDiliics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Dili.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaGazaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Gaza.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaHebronics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Hebron.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaJakartaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jakarta.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaJayapuraics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jayapura.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaMakassarics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Makassar.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaPontianakics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Pontianak.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaUjung_Pandangics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Ujung_Pandang.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeBusingenics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Busingen.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeVaduzics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Vaduz.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeZurichics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Zurich.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoJamaicaics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Jamaica.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoPacificFijiics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Fiji.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoPacificJohnstonics">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Johnston.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfolinkstxt">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/links.txt</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfotimezonesxml">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/timezones.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoversiontxt">CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/version.txt</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoresubpostgrespy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/subpostgres.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoretesttest_subpostgrespy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/test/test_subpostgres.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoreutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastorefilepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/file.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/schedule.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimipinboundpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/inbound.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimiptesttest_inboundpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimplicitpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingitippy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/itip.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingprocessingpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/processing.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingtesttest_implicitpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingutilspy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/utils.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoresqlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretestcommonpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_implicitpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_implicit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_sqlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_utilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcarddavdatastoresqlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcarddavdatastoretestcommonpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/test/common.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastorefilepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/file.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresqlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemacurrentoracledialectsql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current-oracle-dialect.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemacurrentsql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv20sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv21sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv22sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv23sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v23.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_19_to_20sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_13_to_14sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_tablespy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_tables.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoretestutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/test/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqltesttest_upgradepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/test/test_upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrade.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradesaddressbook_upgrade_from_1_to_2py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_1_to_2py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradesutilpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavxmlbasepy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/base.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavxmlrfc6578py">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/rfc6578.py</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestclientsplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/clients.plist</a></li>
<li>CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/</li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigseventsonlyplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesacceptsplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyrecurringplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv24sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv25sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v25.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldpostgresdialectv24sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldpostgresdialectv25sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v25.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigseventsonlyplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesacceptsplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyrecurringplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyplist">CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist</a></li>
</ul>
<h3>Property Changed</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischedule">CalendarServer/branches/users/cdaboo/fix-no-ischedule/</a></li>
<li><a href="#CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresqlpy">CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboofixnoischedule"></a>
<div class="propset"><h4>Property changes: CalendarServer/branches/users/cdaboo/fix-no-ischedule</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/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/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/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"> + /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/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/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:11607-11870
</span><a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarserveraccesslogpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/accesslog.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/accesslog.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/accesslog.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -173,7 +173,7 @@
</span><span class="cx"> formatArgs["t"] = (nowtime - request.timeStamps[0][1]) * 1000
</span><span class="cx">
</span><span class="cx"> if hasattr(request, "extendedLogItems"):
</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('"', "%22")
</span><span class="cx"> v = str(v).replace('"', "%22")
</span><span class="cx"> if " " in v:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarserverprovisionrootpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/provision/root.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/provision/root.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/provision/root.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -94,15 +94,7 @@
</span><span class="cx"> from twext.web2.filter import gzip
</span><span class="cx"> self.contentFilters.append((gzip.gzipfilter, True))
</span><span class="cx">
</span><del>- if not config.EnableKeepAlive:
- def addConnectionClose(request, response):
- response.headers.setHeader("connection", ("close",))
- if request.chanRequest is not None:
- request.chanRequest.channel.setReadPersistent(False)
- return response
- self.contentFilters.append((addConnectionClose, True))
</del><span class="cx">
</span><del>-
</del><span class="cx"> def deadProperties(self):
</span><span class="cx"> if not hasattr(self, "_dead_properties"):
</span><span class="cx"> # Get the property store from super
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushamppushpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/amppush.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/amppush.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/amppush.py        2013-11-01 22:25:30 UTC (rev 11871)
</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="CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushnotifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/notifier.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/notifier.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/notifier.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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"> """
</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("Notifications are enabled: %s %s/%s" % (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("Skipping notification for: %s %s/%s" % (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):
+ """
+ Enqueue a push notification work item on the provided transaction.
+ """
</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="CalendarServerbranchesuserscdaboofixnoischedulecalendarserverpushtesttest_notifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/test/test_notifier.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/test/test_notifier.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/push/test/test_notifier.py        2013-11-01 22:25:30 UTC (rev 11871)
</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, ["/CalDAV/example.com/home1/"])
</ins><span class="cx"> yield self.commit()
</span><del>- self.assertEquals(self.notifierFactory.history, ["/CalDAV/example.com/home1/"])
</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(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
</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="home2")
</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"> "/CalDAV/example.com/home2/"
</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="home2")
</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"> "/CalDAV/example.com/home2/"
</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="home2", 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(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
</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("home1")
</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(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/notification/"])
</span><span class="cx"> )
</span><ins>+ yield self.commit()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/caldav.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/caldav.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/caldav.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -58,13 +58,15 @@
</span><span class="cx"> from twext.internet.ssl import ChainingOpenSSLContextFactory
</span><span class="cx"> from twext.internet.tcp import MaxAcceptTCPServer, MaxAcceptSSLServer
</span><span class="cx"> from twext.internet.fswatch import DirectoryChangeListener, IDirectoryChangeListenee
</span><del>-from twext.web2.channel.http import LimitingHTTPFactory, SSLRedirectRequest
</del><ins>+from twext.web2.channel.http import LimitingHTTPFactory, SSLRedirectRequest, \
+ HTTPChannel
</ins><span class="cx"> from twext.web2.metafd import ConnectionLimiter, ReportingHTTPService
</span><span class="cx"> from twext.enterprise.ienterprise import POSTGRES_DIALECT
</span><span class="cx"> from twext.enterprise.ienterprise import ORACLE_DIALECT
</span><span class="cx"> from twext.enterprise.adbapi2 import ConnectionPool
</span><ins>+from twext.enterprise.queue import NonPerformingQueuer
+from twext.enterprise.queue import PeerConnectionPool
</ins><span class="cx"> from twext.enterprise.queue import WorkerFactory as QueueWorkerFactory
</span><del>-from twext.enterprise.queue import PeerConnectionPool
</del><span class="cx">
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> from txdav.common.datastore.upgrade.sql.upgrade import (
</span><span class="lines">@@ -225,14 +227,32 @@
</span><span class="cx"> """ Registers a rotating file logger for error logging, if
</span><span class="cx"> config.ErrorLogEnabled is True. """
</span><span class="cx">
</span><ins>+ def __init__(self, logEnabled, logPath, logRotateLength, logMaxFiles):
+ """
+ @param logEnabled: Whether to write to a log file
+ @type logEnabled: C{boolean}
+ @param logPath: the full path to the log file
+ @type logPath: C{str}
+ @param logRotateLength: rotate when files exceed this many bytes
+ @type logRotateLength: C{int}
+ @param logMaxFiles: keep at most this many files
+ @type logMaxFiles: C{int}
+ """
+ MultiService.__init__(self)
+ self.logEnabled = logEnabled
+ self.logPath = logPath
+ self.logRotateLength = logRotateLength
+ self.logMaxFiles = logMaxFiles
+
+
</ins><span class="cx"> def setServiceParent(self, app):
</span><span class="cx"> MultiService.setServiceParent(self, app)
</span><span class="cx">
</span><del>- if config.ErrorLogEnabled:
</del><ins>+ if self.logEnabled:
</ins><span class="cx"> errorLogFile = LogFile.fromFullPath(
</span><del>- config.ErrorLogFile,
- rotateLength=config.ErrorLogRotateMB * 1024 * 1024,
- maxRotatedFiles=config.ErrorLogMaxRotatedFiles
</del><ins>+ self.logPath,
+ rotateLength=self.logRotateLength,
+ maxRotatedFiles=self.logMaxFiles
</ins><span class="cx"> )
</span><span class="cx"> errorLogObserver = FileLogObserver(errorLogFile).emit
</span><span class="cx">
</span><span class="lines">@@ -251,7 +271,9 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, logObserver):
</span><span class="cx"> self.logObserver = logObserver # accesslog observer
</span><del>- MultiService.__init__(self)
</del><ins>+ ErrorLoggingMultiService.__init__(self, config.ErrorLogEnabled,
+ config.ErrorLogFile, config.ErrorLogRotateMB * 1024 * 1024,
+ config.ErrorLogMaxRotatedFiles)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def privilegedStartService(self):
</span><span class="lines">@@ -958,6 +980,13 @@
</span><span class="cx"> def requestFactory(*args, **kw):
</span><span class="cx"> return SSLRedirectRequest(site=underlyingSite, *args, **kw)
</span><span class="cx">
</span><ins>+ # Setup HTTP connection behaviors
+ HTTPChannel.allowPersistentConnections = config.EnableKeepAlive
+ HTTPChannel.betweenRequestsTimeOut = config.PipelineIdleTimeOut
+ HTTPChannel.inputTimeOut = config.IncomingDataTimeOut
+ HTTPChannel.idleTimeOut = config.IdleConnectionTimeOut
+ HTTPChannel.closeTimeOut = config.CloseConnectionTimeOut
+
</ins><span class="cx"> # Add the Strict-Transport-Security header to all secured requests
</span><span class="cx"> # if enabled.
</span><span class="cx"> if config.StrictTransportSecuritySeconds:
</span><span class="lines">@@ -971,6 +1000,7 @@
</span><span class="cx"> "max-age={max_age:d}"
</span><span class="cx"> .format(max_age=config.StrictTransportSecuritySeconds))
</span><span class="cx"> return response
</span><ins>+ responseFilter.handleErrors = True
</ins><span class="cx"> request.addResponseFilter(responseFilter)
</span><span class="cx"> return request
</span><span class="cx">
</span><span class="lines">@@ -1182,6 +1212,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 = "tcp:%d:interface=127.0.0.1" % (config.Manhole.StartingPortNumber,)
+ manholeService = manholeMakeService({
+ "sshPort" : None,
+ "telnetPort" : portString,
+ "namespace" : {
+ "config" : config,
+ "service" : result,
+ "store" : store,
+ "directory" : directory,
+ },
+ "passwd" : config.Manhole.PasswordFilePath,
+ })
+ manholeService.setServiceParent(result)
+ # Using print(because logging isn't ready at this point)
+ print("Manhole access enabled: %s" % (portString,))
+ except ImportError:
+ print("Manhole access could not enabled because manhole_tap could not be imported")
+
</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">@@ -1247,8 +1299,9 @@
</span><span class="cx"> Create an agent service which listens for configuration requests
</span><span class="cx"> """
</span><span class="cx">
</span><del>- # Don't use memcached -- calendar server might take it away at any
- # moment
</del><ins>+ # Don't use memcached initially -- calendar server might take it away at
+ # any moment. However, when we run a command through the gateway, it
+ # will conditionally set ClientEnabled at that time.
</ins><span class="cx"> def agentPostUpdateHook(configDict, reloading=False):
</span><span class="cx"> configDict.Memcached.Pools.Default.ClientEnabled = False
</span><span class="cx">
</span><span class="lines">@@ -1266,10 +1319,20 @@
</span><span class="cx"> dataStoreWatcher = DirectoryChangeListener(reactor,
</span><span class="cx"> config.DataRoot, DataStoreMonitor(reactor, storageService))
</span><span class="cx"> dataStoreWatcher.startListening()
</span><ins>+ if store is not None:
+ store.queuer = NonPerformingQueuer()
</ins><span class="cx"> return makeAgentService(store)
</span><span class="cx">
</span><span class="cx"> uid, gid = getSystemIDs(config.UserName, config.GroupName)
</span><del>- return self.storageService(agentServiceCreator, None, uid=uid, gid=gid)
</del><ins>+ svc = self.storageService(agentServiceCreator, None, uid=uid, gid=gid)
+ agentLoggingService = ErrorLoggingMultiService(
+ config.ErrorLogEnabled,
+ config.AgentLogFile,
+ config.ErrorLogRotateMB * 1024 * 1024,
+ config.ErrorLogMaxRotatedFiles
+ )
+ svc.setServiceParent(agentLoggingService)
+ return agentLoggingService
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def storageService(self, createMainService, logObserver, uid=None, gid=None):
</span><span class="lines">@@ -1366,7 +1429,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 class="lines">@@ -1428,7 +1493,12 @@
</span><span class="cx"> Create a master service to coordinate a multi-process configuration,
</span><span class="cx"> spawning subprocesses that use L{makeService_Slave} to perform work.
</span><span class="cx"> """
</span><del>- s = ErrorLoggingMultiService()
</del><ins>+ s = ErrorLoggingMultiService(
+ config.ErrorLogEnabled,
+ config.ErrorLogFile,
+ config.ErrorLogRotateMB * 1024 * 1024,
+ config.ErrorLogMaxRotatedFiles
+ )
</ins><span class="cx">
</span><span class="cx"> # Add a service to re-exec the master when it receives SIGHUP
</span><span class="cx"> ReExecService(config.PIDFile).setServiceParent(s)
</span><span class="lines">@@ -2387,6 +2457,7 @@
</span><span class="cx"> return uid, gid
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class DataStoreMonitor(object):
</span><span class="cx"> implements(IDirectoryChangeListenee)
</span><span class="cx">
</span><span class="lines">@@ -2398,18 +2469,21 @@
</span><span class="cx"> self._reactor = reactor
</span><span class="cx"> self._storageService = storageService
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def disconnected(self):
</span><span class="cx"> self._storageService.hardStop()
</span><span class="cx"> self._reactor.stop()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def deleted(self):
</span><span class="cx"> self._storageService.hardStop()
</span><span class="cx"> self._reactor.stop()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renamed(self):
</span><span class="cx"> self._storageService.hardStop()
</span><span class="cx"> self._reactor.stop()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> pass
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertaputilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tap/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</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">@@ -1088,7 +1089,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def defaultStepWithFailure(self, failure):
</span><del>- log.failure("Step failure", failure=failure)
</del><ins>+ if failure.type != NotAllowedToUpgrade:
+ log.failure("Step failure", 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="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsagentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/agent.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/agent.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/agent.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -243,7 +243,8 @@
</span><span class="cx"> log.warn("Agent inactive; shutting down")
</span><span class="cx"> reactor.stop()
</span><span class="cx">
</span><del>- inactivityDetector = InactivityDetector(reactor, 60 * 10, becameInactive)
</del><ins>+ inactivityDetector = InactivityDetector(reactor,
+ config.AgentInactivityTimeoutSeconds, becameInactive)
</ins><span class="cx"> root = Resource()
</span><span class="cx"> root.putChild("gateway", AgentGatewayResource(store,
</span><span class="cx"> davRootResource, directory, inactivityDetector))
</span><span class="lines">@@ -278,8 +279,9 @@
</span><span class="cx"> self._timeoutSeconds = timeoutSeconds
</span><span class="cx"> self._becameInactive = becameInactive
</span><span class="cx">
</span><del>- self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
- self._inactivityThresholdReached)
</del><ins>+ if self._timeoutSeconds > 0:
+ self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
+ self._inactivityThresholdReached)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _inactivityThresholdReached(self):
</span><span class="lines">@@ -295,19 +297,21 @@
</span><span class="cx"> Call this to let the InactivityMonitor that there has been activity.
</span><span class="cx"> It will reset the timeout.
</span><span class="cx"> """
</span><del>- if self._delayedCall.active():
- self._delayedCall.reset(self._timeoutSeconds)
- else:
- self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
- self._inactivityThresholdReached)
</del><ins>+ if self._timeoutSeconds > 0:
+ if self._delayedCall.active():
+ self._delayedCall.reset(self._timeoutSeconds)
+ else:
+ self._delayedCall = self._reactor.callLater(self._timeoutSeconds,
+ self._inactivityThresholdReached)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def stop(self):
</span><span class="cx"> """
</span><span class="cx"> Cancels the delayed call
</span><span class="cx"> """
</span><del>- if self._delayedCall.active():
- self._delayedCall.cancel()
</del><ins>+ if self._timeoutSeconds > 0:
+ if self._delayedCall.active():
+ self._delayedCall.cancel()
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsdbinspectpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/dbinspect.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/dbinspect.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/dbinspect.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -22,8 +22,6 @@
</span><span class="cx"> of simple commands.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-from caldavclientlibrary.admin.xmlaccounts.recordtypes import recordType_users, \
- recordType_locations, recordType_resources, recordType_groups
</del><span class="cx"> from calendarserver.tools import tables
</span><span class="cx"> from calendarserver.tools.cmdline import utilityMain
</span><span class="cx"> from pycalendar.datetime import PyCalendarDateTime
</span><span class="lines">@@ -38,6 +36,7 @@
</span><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
</span><span class="cx"> from twistedcaldav.directory import calendaruserproxy
</span><ins>+from twistedcaldav.directory.directory import DirectoryService
</ins><span class="cx"> from twistedcaldav.query import calendarqueryfilter
</span><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
</span><span class="lines">@@ -104,13 +103,13 @@
</span><span class="cx"> except (ValueError, TypeError):
</span><span class="cx"> pass
</span><span class="cx">
</span><del>- record = txn.directoryService().recordWithShortName(recordType_users, value)
</del><ins>+ record = txn.directoryService().recordWithShortName(DirectoryService.recordType_users, value)
</ins><span class="cx"> if record is None:
</span><del>- record = txn.directoryService().recordWithShortName(recordType_locations, value)
</del><ins>+ record = txn.directoryService().recordWithShortName(DirectoryService.recordType_locations, value)
</ins><span class="cx"> if record is None:
</span><del>- record = txn.directoryService().recordWithShortName(recordType_resources, value)
</del><ins>+ record = txn.directoryService().recordWithShortName(DirectoryService.recordType_resources, value)
</ins><span class="cx"> if record is None:
</span><del>- record = txn.directoryService().recordWithShortName(recordType_groups, value)
</del><ins>+ record = txn.directoryService().recordWithShortName(DirectoryService.recordType_groups, value)
</ins><span class="cx"> return record.guid if record else None
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsgatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/gateway.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/gateway.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/gateway.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx">
</span><span class="cx"> from calendarserver.tools.util import (
</span><span class="cx"> principalForPrincipalID, proxySubprincipal, addProxy, removeProxy,
</span><del>- ProxyError, ProxyWarning
</del><ins>+ ProxyError, ProxyWarning, autoDisableMemcached
</ins><span class="cx"> )
</span><span class="cx"> from calendarserver.tools.principals import getProxies, setProxies, updateRecord
</span><span class="cx"> from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
</span><span class="lines">@@ -188,6 +188,22 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def run(self):
</span><ins>+
+ # This method can be called as the result of an agent request. We
+ # check to see if memcached is there for each call because the server
+ # could have stopped/started since the last time.
+
+ for pool in config.Memcached.Pools.itervalues():
+ pool.ClientEnabled = True
+ autoDisableMemcached(config)
+
+ from twistedcaldav.directory import calendaruserproxy
+ if calendaruserproxy.ProxyDBService is not None:
+ # Reset the proxy db memcacher because memcached may have come or
+ # gone since the last time through here.
+ # TODO: figure out a better way to do this
+ calendaruserproxy.ProxyDBService._memcacher._memcacheProtocol = None
+
</ins><span class="cx"> try:
</span><span class="cx"> for command in self.commands:
</span><span class="cx"> commandName = command['command']
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolstesttest_agentpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/test/test_agent.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/test/test_agent.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/test/test_agent.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -145,6 +145,9 @@
</span><span class="cx">
</span><span class="cx"> id.stop()
</span><span class="cx">
</span><ins>+ # Verify a timeout of 0 does not ever fire
+ id = InactivityDetector(clock, 0, becameInactive)
+ self.assertEquals(clock.getDelayedCalls(), [])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> class FakeRequest(object):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/upgrade.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/upgrade.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/upgrade.py        2013-11-01 22:25:30 UTC (rev 11871)
</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, "Specify caldavd.plist configuration path."],
</span><ins>+ ['prefix', 'x', "", "Only upgrade homes with the specified GUID prefix - partial upgrade only."],
</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"> """
</span><span class="cx"> Immediately stop. The upgrade will have been run before this.
</span><span class="cx"> """
</span><del>- # If we get this far the database is OK
- if self.options["status"]:
- self.output.write("Database OK.\n")
</del><ins>+ if self.store is None:
+ if self.options["status"]:
+ self.output.write("Upgrade needed.\n")
+ else:
+ self.output.write("Upgrade failed.\n")
</ins><span class="cx"> else:
</span><del>- self.output.write("Upgrade complete, shutting down.\n")
</del><ins>+ # If we get this far the database is OK
+ if self.options["status"]:
+ self.output.write("Database OK.\n")
+ else:
+ self.output.write("Upgrade complete, shutting down.\n")
</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() + " " + text + "\n")
</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["postprocess"]
</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["status"]
</span><ins>+ if options["prefix"]:
+ config.UpgradeHomePrefix = options["prefix"]
</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("Failed to start service.")
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecalendarservertoolsutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/calendarserver/tools/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -235,23 +235,21 @@
</span><span class="cx">
</span><span class="cx"> def autoDisableMemcached(config):
</span><span class="cx"> """
</span><del>- If memcached is not running, set config.Memcached.ClientEnabled to False
</del><ins>+ Set ClientEnabled to False for each pool whose memcached is not running
</ins><span class="cx"> """
</span><span class="cx">
</span><del>- if not config.Memcached.Pools.Default.ClientEnabled:
- return
</del><ins>+ for pool in config.Memcached.Pools.itervalues():
+ if pool.ClientEnabled:
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((pool.BindAddress, pool.Port))
+ s.close()
</ins><span class="cx">
</span><del>- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
</del><ins>+ except socket.error:
+ pool.ClientEnabled = False
</ins><span class="cx">
</span><del>- try:
- s.connect((config.Memcached.Pools.Default.BindAddress, config.Memcached.Pools.Default.Port))
- s.close()
</del><span class="cx">
</span><del>- except socket.error:
- config.Memcached.Pools.Default.ClientEnabled = False
</del><span class="cx">
</span><del>-
-
</del><span class="cx"> def setupMemcached(config):
</span><span class="cx"> #
</span><span class="cx"> # Connect to memcached
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduleconfauthaccountstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/auth/accounts-test.xml (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/auth/accounts-test.xml        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/auth/accounts-test.xml        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -89,7 +89,7 @@
</span><span class="cx"> <first-name>ま</first-name>
</span><span class="cx"> <last-name>だ</last-name>
</span><span class="cx"> </user>
</span><del>- <user repeat="99">
</del><ins>+ <user repeat="101">
</ins><span class="cx"> <uid>user%02d</uid>
</span><span class="cx"> <uid>User %02d</uid>
</span><span class="cx"> <guid>user%02d</guid>
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduleconfcaldavdappleplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/caldavd-apple.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/caldavd-apple.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/conf/caldavd-apple.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -111,11 +111,18 @@
</span><span class="cx"> <string>-c log_lock_waits=TRUE</string>
</span><span class="cx"> <string>-c deadlock_timeout=10</string>
</span><span class="cx"> <string>-c log_line_prefix='%m [%p] '</string>
</span><ins>+ <string>-c logging_collector=on</string>
+ <string>-c log_truncate_on_rotation=on</string>
+ <string>-c log_directory=/var/log/caldavd/postgresql</string>
+ <string>-c log_filename=postgresql_%w.log</string>
+ <string>-c log_rotation_age=1440</string>
</ins><span class="cx"> </array>
</span><span class="cx"> <key>ExtraConnections</key>
</span><span class="cx"> <integer>20</integer>
</span><span class="cx"> <key>ClusterName</key>
</span><span class="cx"> <string>cluster.pg</string>
</span><ins>+ <key>LogFile</key>
+ <string>xpg_ctl.log</string>
</ins><span class="cx"> </dict>
</span><span class="cx">
</span><span class="cx"> <!-- Data root -->
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestclientsplistfromrev11870CalendarServertrunkcontribperformanceloadtestclientsplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/clients.plist (from rev 11870, CalendarServer/trunk/contrib/performance/loadtest/clients.plist) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/clients.plist         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/clients.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,445 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2011-2013 Apple Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+        <dict>
+                <!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. -->
+                <key>clients</key>
+
+                <!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict -->
+                <array>
+
+                        <dict>
+
+                                <!-- Here is a OS X client simulator. -->
+                                <key>software</key>
+                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+
+                                <!-- Arguments to use to initialize the OS_X_10_7 instance. -->
+                                <key>params</key>
+                                <dict>
+                                        <!-- Name that appears in logs. -->
+                                        <key>title</key>
+                                        <string>10.7</string>
+        
+                                        <!-- OS_X_10_7 can poll the calendar home at some interval. This is
+                                                in seconds. -->
+                                        <key>calendarHomePollInterval</key>
+                                        <integer>30</integer>
+
+                                        <!-- 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. -->
+                                        <key>supportPush</key>
+                                        <false />
+
+                                        <key>supportAmpPush</key>
+                                        <true/>
+                                        <key>ampPushHost</key>
+                                        <string>localhost</string>
+                                        <key>ampPushPort</key>
+                                        <integer>62311</integer>
+                                </dict>
+
+                                <!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. -->
+                                <key>profiles</key>
+                                <array>
+
+                                        <!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. -->
+                                                        <key>interval</key>
+                                                        <integer>60</integer>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <true/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile invites some number of new attendees to new events. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the frequency at which new invitations will be sent out. -->
+                                                        <key>sendInvitationDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.NormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
+                                                                        <key>mu</key>
+                                                                        <integer>60</integer>
+
+                                                                        <!-- and sigma gives its standard deviation. -->
+                                                                        <key>sigma</key>
+                                                                        <integer>5</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users "close to" the organizer based on account index. If the clumping
+                                                                is too "tight" for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        -->
+                                                        <key>inviteeDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
+                                                                        <key>min</key>
+                                                                        <integer>0</integer>
+                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
+                                                                        <key>max</key>
+                                                                        <integer>99</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <key>inviteeClumping</key>
+                                                        <true/>
+
+                                                        <!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                         -->
+                                                        <key>inviteeCountDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>1</integer>
+                                                                        <!-- mean - average-->
+                                                                        <key>median</key>
+                                                                        <integer>6</integer>
+                                                                        <!-- maximum -->
+                                                                        <key>maximum</key>
+                                                                        <real>60</real>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <true/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile accepts invitations to events, handles cancels, and
+                                         handles replies received. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        -->
+                                                        <key>acceptDelayDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>300</integer>
+                                                                        <!-- median - 50% done-->
+                                                                        <key>median</key>
+                                                                        <integer>1800</integer>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. -->
+                                                        <key>interval</key>
+                                                        <integer>300</integer>
+
+                                                        <!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>taskDueDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                </array>
+
+                                <!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. -->
+                                <key>weight</key>
+                                <integer>1</integer>
+                        </dict>
+                </array>
+        </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestconfigdistplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.dist.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.dist.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.dist.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -50,10 +50,19 @@
</span><span class="cx">                         <integer>8080</integer>
</span><span class="cx">                 </dict>
</span><span class="cx">
</span><del>-                <!-- Define whether client data should be saved and re-used. -->
</del><ins>+                <!-- Define whether server supports stats socket. -->
+                <key>serverStats</key>
+                <dict>
+                        <key>enabled</key>
+                        <true/>
+                        <key>Port</key>
+                        <integer>8100</integer>
+                </dict>
+
+                <!-- Define whether client data should be re-used. It will always be saved to the specified path.-->
</ins><span class="cx">                 <key>clientDataSerialization</key>
</span><span class="cx">                 <dict>
</span><del>-                        <key>Enabled</key>
</del><ins>+                        <key>UseOldData</key>
</ins><span class="cx">                         <true/>
</span><span class="cx">                         <key>Path</key>
</span><span class="cx">                         <string>/tmp/sim</string>
</span><span class="lines">@@ -119,471 +128,6 @@
</span><span class="cx">
</span><span class="cx">                 </dict>
</span><span class="cx">
</span><del>-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a OS X client simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the OS_X_10_7 instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-
-                                        <!-- OS_X_10_7 can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>30</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>60</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites new attendees to existing events.
-                                         This profile should no longer be used - use RealisticInviter instead. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Inviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.NormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
-                                                                        <key>mu</key>
-                                                                        <integer>60</integer>
-
-                                                                        <!-- and sigma gives its standard deviation. -->
-                                                                        <key>sigma</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- 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. -->
-                                                        <key>inviteeDistanceDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>-100</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>101</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.NormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
-                                                                        <key>mu</key>
-                                                                        <integer>60</integer>
-
-                                                                        <!-- and sigma gives its standard deviation. -->
-                                                                        <key>sigma</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>-100</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>101</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>1</integer>
-                                                                        <!-- mean - average-->
-                                                                        <key>median</key>
-                                                                        <integer>6</integer>
-                                                                        <!-- maximum -->
-                                                                        <key>maximum</key>
-                                                                        <real>100</real>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>300</integer>
-                                                                        <!-- median - 50% done-->
-                                                                        <key>median</key>
-                                                                        <integer>1800</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-
</del><span class="cx">                 <!-- Define some log observers to report on the load test. -->
</span><span class="cx">                 <key>observers</key>
</span><span class="cx">                 <array>
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestconfigplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/config.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -37,10 +37,19 @@
</span><span class="cx">                         <integer>8080</integer>
</span><span class="cx">                 </dict>
</span><span class="cx">
</span><del>-                <!-- Define whether client data should be saved and re-used. -->
</del><ins>+                <!-- Define whether server supports stats socket. -->
+                <key>serverStats</key>
+                <dict>
+                        <key>enabled</key>
+                        <true/>
+                        <key>Port</key>
+                        <integer>8100</integer>
+                </dict>
+
+                <!-- Define whether client data should be re-used. It will always be saved to the specified path.-->
</ins><span class="cx">                 <key>clientDataSerialization</key>
</span><span class="cx">                 <dict>
</span><del>-                        <key>Enabled</key>
</del><ins>+                        <key>UseOldData</key>
</ins><span class="cx">                         <true/>
</span><span class="cx">                         <key>Path</key>
</span><span class="cx">                         <string>/tmp/sim</string>
</span><span class="lines">@@ -106,429 +115,6 @@
</span><span class="cx">
</span><span class="cx">                 </dict>
</span><span class="cx">
</span><del>-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a OS X client simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the OS_X_10_7 instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-        
-                                        <!-- OS_X_10_7 can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>30</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-
-                                        <key>supportAmpPush</key>
-                                        <true/>
-                                        <key>ampPushHost</key>
-                                        <string>localhost</string>
-                                        <key>ampPushPort</key>
-                                        <integer>62311</integer>
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>60</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.NormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
-                                                                        <key>mu</key>
-                                                                        <integer>60</integer>
-
-                                                                        <!-- and sigma gives its standard deviation. -->
-                                                                        <key>sigma</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>0</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>99</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>1</integer>
-                                                                        <!-- mean - average-->
-                                                                        <key>median</key>
-                                                                        <integer>6</integer>
-                                                                        <!-- maximum -->
-                                                                        <key>maximum</key>
-                                                                        <real>60</real>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>300</integer>
-                                                                        <!-- median - 50% done-->
-                                                                        <key>median</key>
-                                                                        <integer>1800</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-
</del><span class="cx">                 <!-- Define some log observers to report on the load test. -->
</span><span class="cx">                 <key>observers</key>
</span><span class="cx">                 <array>
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestpopulationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/population.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/population.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/population.py        2013-11-01 22:25:30 UTC (rev 11871)
</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 "thresholdsPath" in params:
</span><span class="lines">@@ -423,6 +424,13 @@
</span><span class="cx"> self._fail_cut_off = params["failCutoff"]
</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 = "%" + maxColumnWidth + "s : %-s\n"
</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 "None"
</del><ins>+ return ("%-8.4f" % mean(differences)) if differences else "None"
</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': "%02d:%02d:%02d" % (runHours, runMinutes, runSeconds),
</span><span class="cx"> 'CPU Time': "user %-5.2f sys %-5.2f total %02d:%02d:%02d" % (cpuUser, cpuSys, cpuHours, cpuMinutes, cpuSeconds,),
</span><del>- 'QoS': "%-8.4f" % (self.qos(),),
</del><ins>+ 'QoS': self.qos(),
</ins><span class="cx"> }
</span><span class="cx"> if self.countClientFailures() > 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() > 0:
</span><span class="cx"> for reason, count in self._failed_sim.items():
</span><span class="cx"> items['Failed operation'] = "%s : %d times" % (reason, count,)
</span><ins>+ output.write("* Client\n")
</ins><span class="cx"> self.printMiscellaneous(output, items)
</span><span class="cx"> output.write("\n")
</span><ins>+
+ if self._expired_data is not None:
+ items = {
+ "Req/sec" : "%.1f" % (self._expired_data[0],),
+ "Response": "%.1f (ms)" % (self._expired_data[1],),
+ "Slots": "%.2f" % (self._expired_data[2],),
+ "CPU": "%.1f%%" % (self._expired_data[3],),
+ }
+ output.write("* Server (Last 5 minutes)\n")
+ self.printMiscellaneous(output, items)
+ output.write("\n")
+ output.write("* Details\n")
+
</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="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtestsimpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/sim.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/sim.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/sim.py        2013-11-01 22:25:30 UTC (rev 11871)
</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="user%d", passwordPattern="user%d",
</span><span class="cx"> namePattern="User %d", emailPattern="user%d@example.com"):
</span><span class="cx"> for i in xrange(count):
</span><span class="lines">@@ -121,6 +130,7 @@
</span><span class="cx"> """
</span><span class="cx"> config = None
</span><span class="cx"> _defaultConfig = FilePath(__file__).sibling("config.plist")
</span><ins>+ _defaultClients = FilePath(__file__).sibling("clients.plist")
</ins><span class="cx">
</span><span class="cx"> optParameters = [
</span><span class="cx"> ("runtime", "t", None,
</span><span class="lines">@@ -129,6 +139,9 @@
</span><span class="cx"> ("config", None, _defaultConfig,
</span><span class="cx"> "Configuration plist file name from which to read simulation parameters.",
</span><span class="cx"> FilePath),
</span><ins>+ ("clients", None, _defaultClients,
+ "Configuration plist file name from which to read client parameters.",
+ 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("--clients %s: %s" % (
+ self['clients'].path, e.strerror))
+ try:
+ try:
+ client_config = readPlist(clientFile)
+ self.config["clients"] = client_config["clients"]
+ if "arrivalInterval" in client_config:
+ self.config["arrival"]["params"]["interval"] = client_config["arrivalInterval"]
+ except ExpatError, e:
+ raise UsageError("--clients %s: %s" % (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"> """
</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("Unable to create client data serialization directory: %s" % (serializationPath))
- print("Please consult the clientDataSerialization stanza of contrib/performance/loadtest/config.plist")
- 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("Unable to create client data serialization directory: %s" % (serializationPath))
+ print("Please consult the clientDataSerialization stanza of contrib/performance/loadtest/config.plist")
+ 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("Loaded {0} accounts.\n".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):
+ """
+ Capture server stats and stop.
+ """
</ins><span class="cx">
</span><ins>+ if self.serverStats is not None:
+ _ignore_scheme, hostname, _ignore_path, _ignore_query, _ignore_fragment = urlsplit(self.serverStats["server"])
+ data = self.readStatsSock((hostname.split(":")[0], self.serverStats["Port"],), True)
+ if "Failed" not in data:
+ data = data["5 Minutes"]
+ result = (
+ safeDivision(float(data["requests"]), 5 * 60),
+ safeDivision(data["t"], data["requests"]),
+ safeDivision(float(data["slots"]), data["requests"]),
+ safeDivision(data["cpu"], data["requests"]),
+ )
+ msg(type="sim-expired", reason=result)
+
+
+ def stopAndReport(self):
+ """
+ Runtime has expired - capture server stats and stop.
+ """
+
+ 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 = ""
+ while True:
+ d = s.recv(1024)
+ if d:
+ data += d
+ else:
+ break
+ s.close()
+ data = json.loads(data)
+ except socket.error:
+ data = {"Failed": "Unable to read statistics from server: %s" % (sockname,)}
+ data["Server"] = sockname
+ return data
+
+
+
</ins><span class="cx"> def attachService(reactor, loadsim, service):
</span><span class="cx"> """
</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("stderr received from " + str(self.transport.pid))
</span><span class="cx"> msg(" " + repr(error))
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigseventsonlyplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1,440 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-        <dict>
-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a Lion iCal simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the client instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-
-                                        <!-- Client can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>300000</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-                                        <key>supportAmpPush</key>
-                                        <false />
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>20</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.NormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
-                                                                        <key>mu</key>
-                                                                        <integer>10</integer>
-
-                                                                        <!-- and sigma gives its standard deviation. -->
-                                                                        <key>sigma</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>0</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>99</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>1</integer>
-                                                                        <!-- mean - average-->
-                                                                        <key>median</key>
-                                                                        <integer>6</integer>
-                                                                        <!-- maximum -->
-                                                                        <key>maximum</key>
-                                                                        <real>100</real>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>300</integer>
-                                                                        <!-- median - 50% done-->
-                                                                        <key>median</key>
-                                                                        <integer>1800</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-        </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigseventsonlyplistfromrev11870CalendarServertrunkcontribperformanceloadteststandardconfigseventsonlyplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist (from rev 11870, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/events-only.plist) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/events-only.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,440 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+        <dict>
+                <!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. -->
+                <key>clients</key>
+
+                <!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict -->
+                <array>
+
+                        <dict>
+
+                                <!-- Here is a Lion iCal simulator. -->
+                                <key>software</key>
+                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+
+                                <!-- Arguments to use to initialize the client instance. -->
+                                <key>params</key>
+                                <dict>
+                                        <!-- Name that appears in logs. -->
+                                        <key>title</key>
+                                        <string>10.7</string>
+
+                                        <!-- Client can poll the calendar home at some interval. This is
+                                                in seconds. -->
+                                        <key>calendarHomePollInterval</key>
+                                        <integer>300000</integer>
+
+                                        <!-- 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. -->
+                                        <key>supportPush</key>
+                                        <false />
+                                        <key>supportAmpPush</key>
+                                        <false />
+                                </dict>
+
+                                <!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. -->
+                                <key>profiles</key>
+                                <array>
+
+                                        <!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. -->
+                                                        <key>interval</key>
+                                                        <integer>20</integer>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile invites some number of new attendees to new events. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the frequency at which new invitations will be sent out. -->
+                                                        <key>sendInvitationDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.NormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mu gives the mean of the normal distribution (in seconds). -->
+                                                                        <key>mu</key>
+                                                                        <integer>10</integer>
+
+                                                                        <!-- and sigma gives its standard deviation. -->
+                                                                        <key>sigma</key>
+                                                                        <integer>5</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users "close to" the organizer based on account index. If the clumping
+                                                                is too "tight" for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        -->
+                                                        <key>inviteeDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
+                                                                        <key>min</key>
+                                                                        <integer>0</integer>
+                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
+                                                                        <key>max</key>
+                                                                        <integer>99</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <key>inviteeClumping</key>
+                                                        <true/>
+
+                                                        <!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                         -->
+                                                        <key>inviteeCountDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>1</integer>
+                                                                        <!-- mean - average-->
+                                                                        <key>median</key>
+                                                                        <integer>6</integer>
+                                                                        <!-- maximum -->
+                                                                        <key>maximum</key>
+                                                                        <real>100</real>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <true/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile accepts invitations to events, handles cancels, and
+                                         handles replies received. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        -->
+                                                        <key>acceptDelayDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>300</integer>
+                                                                        <!-- median - 50% done-->
+                                                                        <key>median</key>
+                                                                        <integer>1800</integer>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. -->
+                                                        <key>interval</key>
+                                                        <integer>300</integer>
+
+                                                        <!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>taskDueDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                </array>
+
+                                <!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. -->
+                                <key>weight</key>
+                                <integer>1</integer>
+                        </dict>
+                </array>
+        </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesacceptsplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1,419 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-        <dict>
-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a Lion iCal simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the client instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-
-                                        <!-- Client can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>300000</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-                                        <key>supportAmpPush</key>
-                                        <true />
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>20</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- interval (in seconds). -->
-                                                                        <key>value</key>
-                                                                        <integer>150</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>0</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>99</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- Number of attendees. -->
-                                                                        <key>value</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>100</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformDiscreteDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- Set of values to use - will be chosen in random order. -->
-                                                                        <key>values</key>
-                                                                        <array>
-                                                                                <integer>0</integer>
-                                                                                <integer>5</integer>
-                                                                                <integer>10</integer>
-                                                                                <integer>15</integer>
-                                                                                <integer>20</integer>
-                                                                                <integer>25</integer>
-                                                                                <integer>30</integer>
-                                                                        </array>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-
-                <!-- Determine the interval between client creation. -->
-                <key>arrivalInterval</key>
-                <integer>5</integer>
-        </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesacceptsplistfromrev11870CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesacceptsplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist (from rev 11870, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-accepts.plist) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-accepts.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,419 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+        <dict>
+                <!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. -->
+                <key>clients</key>
+
+                <!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict -->
+                <array>
+
+                        <dict>
+
+                                <!-- Here is a Lion iCal simulator. -->
+                                <key>software</key>
+                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+
+                                <!-- Arguments to use to initialize the client instance. -->
+                                <key>params</key>
+                                <dict>
+                                        <!-- Name that appears in logs. -->
+                                        <key>title</key>
+                                        <string>10.7</string>
+
+                                        <!-- Client can poll the calendar home at some interval. This is
+                                                in seconds. -->
+                                        <key>calendarHomePollInterval</key>
+                                        <integer>300000</integer>
+
+                                        <!-- 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. -->
+                                        <key>supportPush</key>
+                                        <false />
+                                        <key>supportAmpPush</key>
+                                        <true />
+                                </dict>
+
+                                <!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. -->
+                                <key>profiles</key>
+                                <array>
+
+                                        <!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. -->
+                                                        <key>interval</key>
+                                                        <integer>20</integer>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile invites some number of new attendees to new events. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the frequency at which new invitations will be sent out. -->
+                                                        <key>sendInvitationDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- interval (in seconds). -->
+                                                                        <key>value</key>
+                                                                        <integer>150</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users "close to" the organizer based on account index. If the clumping
+                                                                is too "tight" for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        -->
+                                                        <key>inviteeDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
+                                                                        <key>min</key>
+                                                                        <integer>0</integer>
+                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
+                                                                        <key>max</key>
+                                                                        <integer>99</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <key>inviteeClumping</key>
+                                                        <true/>
+
+                                                        <!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                         -->
+                                                        <key>inviteeCountDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- Number of attendees. -->
+                                                                        <key>value</key>
+                                                                        <integer>5</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>100</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile accepts invitations to events, handles cancels, and
+                                         handles replies received. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        -->
+                                                        <key>acceptDelayDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformDiscreteDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- Set of values to use - will be chosen in random order. -->
+                                                                        <key>values</key>
+                                                                        <array>
+                                                                                <integer>0</integer>
+                                                                                <integer>5</integer>
+                                                                                <integer>10</integer>
+                                                                                <integer>15</integer>
+                                                                                <integer>20</integer>
+                                                                                <integer>25</integer>
+                                                                                <integer>30</integer>
+                                                                        </array>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. -->
+                                                        <key>interval</key>
+                                                        <integer>300</integer>
+
+                                                        <!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>taskDueDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                </array>
+
+                                <!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. -->
+                                <key>weight</key>
+                                <integer>1</integer>
+                        </dict>
+                </array>
+
+                <!-- Determine the interval between client creation. -->
+                <key>arrivalInterval</key>
+                <integer>5</integer>
+        </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyrecurringplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1,414 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-        <dict>
-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a Lion iCal simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the client instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-
-                                        <!-- Client can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>300000</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-                                        <key>supportAmpPush</key>
-                                        <false />
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>20</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- interval (in seconds). -->
-                                                                        <key>value</key>
-                                                                        <integer>120</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>0</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>99</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- Number of attendees. -->
-                                                                        <key>value</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <true/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>100</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>300</integer>
-                                                                        <!-- median - 50% done-->
-                                                                        <key>median</key>
-                                                                        <integer>1800</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-
-                <!-- Determine the interval between client creation. -->
-                <key>arrivalInterval</key>
-                <integer>4</integer>
-        </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyrecurringplistfromrev11870CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesonlyrecurringplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist (from rev 11870, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only-recurring.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,414 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+        <dict>
+                <!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. -->
+                <key>clients</key>
+
+                <!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict -->
+                <array>
+
+                        <dict>
+
+                                <!-- Here is a Lion iCal simulator. -->
+                                <key>software</key>
+                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+
+                                <!-- Arguments to use to initialize the client instance. -->
+                                <key>params</key>
+                                <dict>
+                                        <!-- Name that appears in logs. -->
+                                        <key>title</key>
+                                        <string>10.7</string>
+
+                                        <!-- Client can poll the calendar home at some interval. This is
+                                                in seconds. -->
+                                        <key>calendarHomePollInterval</key>
+                                        <integer>300000</integer>
+
+                                        <!-- 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. -->
+                                        <key>supportPush</key>
+                                        <false />
+                                        <key>supportAmpPush</key>
+                                        <false />
+                                </dict>
+
+                                <!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. -->
+                                <key>profiles</key>
+                                <array>
+
+                                        <!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. -->
+                                                        <key>interval</key>
+                                                        <integer>20</integer>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile invites some number of new attendees to new events. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the frequency at which new invitations will be sent out. -->
+                                                        <key>sendInvitationDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- interval (in seconds). -->
+                                                                        <key>value</key>
+                                                                        <integer>120</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users "close to" the organizer based on account index. If the clumping
+                                                                is too "tight" for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        -->
+                                                        <key>inviteeDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
+                                                                        <key>min</key>
+                                                                        <integer>0</integer>
+                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
+                                                                        <key>max</key>
+                                                                        <integer>99</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <key>inviteeClumping</key>
+                                                        <true/>
+
+                                                        <!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                         -->
+                                                        <key>inviteeCountDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- Number of attendees. -->
+                                                                        <key>value</key>
+                                                                        <integer>5</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <true/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>100</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile accepts invitations to events, handles cancels, and
+                                         handles replies received. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        -->
+                                                        <key>acceptDelayDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>300</integer>
+                                                                        <!-- median - 50% done-->
+                                                                        <key>median</key>
+                                                                        <integer>1800</integer>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. -->
+                                                        <key>interval</key>
+                                                        <integer>300</integer>
+
+                                                        <!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>taskDueDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                </array>
+
+                                <!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. -->
+                                <key>weight</key>
+                                <integer>1</integer>
+                        </dict>
+                </array>
+
+                <!-- Determine the interval between client creation. -->
+                <key>arrivalInterval</key>
+                <integer>4</integer>
+        </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1,430 +0,0 @@
</span><del>-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright (c) 2011-2012 Apple Inc. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-        <dict>
-                <!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. -->
-                <key>clients</key>
-
-                <!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict -->
-                <array>
-
-                        <dict>
-
-                                <!-- Here is a Lion iCal simulator. -->
-                                <key>software</key>
-                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
-
-                                <!-- Arguments to use to initialize the client instance. -->
-                                <key>params</key>
-                                <dict>
-                                        <!-- Name that appears in logs. -->
-                                        <key>title</key>
-                                        <string>10.7</string>
-
-                                        <!-- Client can poll the calendar home at some interval. This is
-                                                in seconds. -->
-                                        <key>calendarHomePollInterval</key>
-                                        <integer>300000</integer>
-
-                                        <!-- 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. -->
-                                        <key>supportPush</key>
-                                        <false />
-                                        <key>supportAmpPush</key>
-                                        <false />
-                                </dict>
-
-                                <!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. -->
-                                <key>profiles</key>
-                                <array>
-
-                                        <!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. -->
-                                                        <key>interval</key>
-                                                        <integer>20</integer>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile invites some number of new attendees to new events. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <true/>
-
-                                                        <!-- Define the frequency at which new invitations will be sent out. -->
-                                                        <key>sendInvitationDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- interval (in seconds). -->
-                                                                        <key>value</key>
-                                                                        <integer>120</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users "close to" the organizer based on account index. If the clumping
-                                                                is too "tight" for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        -->
-                                                        <key>inviteeDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
-                                                                        <key>min</key>
-                                                                        <integer>0</integer>
-                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
-                                                                        <key>max</key>
-                                                                        <integer>99</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <key>inviteeClumping</key>
-                                                        <true/>
-
-                                                        <!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                         -->
-                                                        <key>inviteeCountDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.FixedDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- Number of attendees. -->
-                                                                        <key>value</key>
-                                                                        <integer>5</integer>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>eventStartDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-
-                                                        <!-- Define how recurrences are created. -->
-                                                        <key>recurrenceDistribution</key>
-                                                        <dict>
-
-                                                                <!-- This distribution is pretty specialized. We have a fixed set of
-                                                                 RRULEs defined for this distribution and pick each based on a
-                                                                 weight. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- False to disable RRULEs -->
-                                                                        <key>allowRecurrence</key>
-                                                                        <false/>
-
-                                                                        <!-- These are the weights for the specific set of RRULEs. -->
-                                                                        <key>weights</key>
-                                                                        <dict>
-                                                                                <!-- Half of all events will be non-recurring -->
-                                                                                <key>none</key>
-                                                                                <integer>50</integer>
-                                                                                
-                                                                                <!-- Daily and weekly are pretty common -->
-                                                                                <key>daily</key>
-                                                                                <integer>10</integer>
-                                                                                <key>weekly</key>
-                                                                                <integer>20</integer>
-                                                                                
-                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
-                                                                                <key>monthly</key>
-                                                                                <integer>2</integer>
-                                                                                <key>yearly</key>
-                                                                                <integer>1</integer>
-                                                                                <key>dailylimit</key>
-                                                                                <integer>2</integer>
-                                                                                <key>weeklylimit</key>
-                                                                                <integer>5</integer>
-                                                                                
-                                                                                <!-- Work days pretty common -->
-                                                                                <key>workdays</key>
-                                                                                <integer>10</integer>
-                                                                        </dict>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- This profile accepts invitations to events, handles cancels, and
-                                         handles replies received. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        -->
-                                                        <key>acceptDelayDistribution</key>
-                                                        <dict>
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- mode - peak-->
-                                                                        <key>mode</key>
-                                                                        <integer>300</integer>
-                                                                        <!-- median - 50% done-->
-                                                                        <key>median</key>
-                                                                        <integer>1800</integer>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                        <!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. -->
-                                        <dict>
-                                                <key>class</key>
-                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
-
-                                                <key>params</key>
-                                                <dict>
-                                                        <key>enabled</key>
-                                                        <false/>
-
-                                                        <!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. -->
-                                                        <key>interval</key>
-                                                        <integer>300</integer>
-
-                                                        <!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a "Distribution" parameter. The value
-                                                                for most "Distribution" parameters are interchangeable and extensible. -->
-                                                        <key>taskDueDistribution</key>
-                                                        <dict>
-
-                                                                <!-- 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. -->
-                                                                <key>type</key>
-                                                                <string>contrib.performance.stats.WorkDistribution</string>
-
-                                                                <key>params</key>
-                                                                <dict>
-                                                                        <!-- These are the days of the week the distribution will use. -->
-                                                                        <key>daysOfWeek</key>
-                                                                        <array>
-                                                                                <string>mon</string>
-                                                                                <string>tue</string>
-                                                                                <string>wed</string>
-                                                                                <string>thu</string>
-                                                                                <string>fri</string>
-                                                                        </array>
-
-                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
-                                                                        <key>beginHour</key>
-                                                                        <integer>8</integer>
-
-                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). -->
-                                                                        <key>endHour</key>
-                                                                        <integer>16</integer>
-
-                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) -->
-                                                                        <key>tzname</key>
-                                                                        <string>America/Los_Angeles</string>
-                                                                </dict>
-                                                        </dict>
-                                                </dict>
-                                        </dict>
-
-                                </array>
-
-                                <!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. -->
-                                <key>weight</key>
-                                <integer>1</integer>
-                        </dict>
-                </array>
-        </dict>
-</plist>
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadteststandardconfigsinvitesonlyplistfromrev11870CalendarServertrunkcontribperformanceloadteststandardconfigsinvitesonlyplist"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist (from rev 11870, CalendarServer/trunk/contrib/performance/loadtest/standard-configs/invites-only.plist) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/standard-configs/invites-only.plist        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,430 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2011-2012 Apple Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+        <dict>
+                <!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. -->
+                <key>clients</key>
+
+                <!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict -->
+                <array>
+
+                        <dict>
+
+                                <!-- Here is a Lion iCal simulator. -->
+                                <key>software</key>
+                                <string>contrib.performance.loadtest.ical.OS_X_10_7</string>
+
+                                <!-- Arguments to use to initialize the client instance. -->
+                                <key>params</key>
+                                <dict>
+                                        <!-- Name that appears in logs. -->
+                                        <key>title</key>
+                                        <string>10.7</string>
+
+                                        <!-- Client can poll the calendar home at some interval. This is
+                                                in seconds. -->
+                                        <key>calendarHomePollInterval</key>
+                                        <integer>300000</integer>
+
+                                        <!-- 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. -->
+                                        <key>supportPush</key>
+                                        <false />
+                                        <key>supportAmpPush</key>
+                                        <false />
+                                </dict>
+
+                                <!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. -->
+                                <key>profiles</key>
+                                <array>
+
+                                        <!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Eventer</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. -->
+                                                        <key>interval</key>
+                                                        <integer>20</integer>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile invites some number of new attendees to new events. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.RealisticInviter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <true/>
+
+                                                        <!-- Define the frequency at which new invitations will be sent out. -->
+                                                        <key>sendInvitationDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- interval (in seconds). -->
+                                                                        <key>value</key>
+                                                                        <integer>120</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users "close to" the organizer based on account index. If the clumping
+                                                                is too "tight" for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        -->
+                                                        <key>inviteeDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.UniformIntegerDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- The minimum value (inclusive) of the uniform distribution. -->
+                                                                        <key>min</key>
+                                                                        <integer>0</integer>
+                                                                        <!-- The maximum value (exclusive) of the uniform distribution. -->
+                                                                        <key>max</key>
+                                                                        <integer>99</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <key>inviteeClumping</key>
+                                                        <true/>
+
+                                                        <!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal "mode" is the peak, "mean" is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                         -->
+                                                        <key>inviteeCountDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.FixedDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- Number of attendees. -->
+                                                                        <key>value</key>
+                                                                        <integer>5</integer>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>eventStartDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+
+                                                        <!-- Define how recurrences are created. -->
+                                                        <key>recurrenceDistribution</key>
+                                                        <dict>
+
+                                                                <!-- This distribution is pretty specialized. We have a fixed set of
+                                                                 RRULEs defined for this distribution and pick each based on a
+                                                                 weight. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.RecurrenceDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- False to disable RRULEs -->
+                                                                        <key>allowRecurrence</key>
+                                                                        <false/>
+
+                                                                        <!-- These are the weights for the specific set of RRULEs. -->
+                                                                        <key>weights</key>
+                                                                        <dict>
+                                                                                <!-- Half of all events will be non-recurring -->
+                                                                                <key>none</key>
+                                                                                <integer>50</integer>
+                                                                                
+                                                                                <!-- Daily and weekly are pretty common -->
+                                                                                <key>daily</key>
+                                                                                <integer>10</integer>
+                                                                                <key>weekly</key>
+                                                                                <integer>20</integer>
+                                                                                
+                                                                                <!-- Monthly, yearly, daily & weekly limit not so common -->
+                                                                                <key>monthly</key>
+                                                                                <integer>2</integer>
+                                                                                <key>yearly</key>
+                                                                                <integer>1</integer>
+                                                                                <key>dailylimit</key>
+                                                                                <integer>2</integer>
+                                                                                <key>weeklylimit</key>
+                                                                                <integer>5</integer>
+                                                                                
+                                                                                <!-- Work days pretty common -->
+                                                                                <key>workdays</key>
+                                                                                <integer>10</integer>
+                                                                        </dict>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- This profile accepts invitations to events, handles cancels, and
+                                         handles replies received. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Accepter</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal "mode" is the peak, "median" is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        -->
+                                                        <key>acceptDelayDistribution</key>
+                                                        <dict>
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.LogNormalDistribution</string>
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- mode - peak-->
+                                                                        <key>mode</key>
+                                                                        <integer>300</integer>
+                                                                        <!-- median - 50% done-->
+                                                                        <key>median</key>
+                                                                        <integer>1800</integer>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                        <!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. -->
+                                        <dict>
+                                                <key>class</key>
+                                                <string>contrib.performance.loadtest.profiles.Tasker</string>
+
+                                                <key>params</key>
+                                                <dict>
+                                                        <key>enabled</key>
+                                                        <false/>
+
+                                                        <!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. -->
+                                                        <key>interval</key>
+                                                        <integer>300</integer>
+
+                                                        <!-- Define how due times (DUE) for the randomly generated tasks
+                                                                will be selected. This is an example of a "Distribution" parameter. The value
+                                                                for most "Distribution" parameters are interchangeable and extensible. -->
+                                                        <key>taskDueDistribution</key>
+                                                        <dict>
+
+                                                                <!-- 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. -->
+                                                                <key>type</key>
+                                                                <string>contrib.performance.stats.WorkDistribution</string>
+
+                                                                <key>params</key>
+                                                                <dict>
+                                                                        <!-- These are the days of the week the distribution will use. -->
+                                                                        <key>daysOfWeek</key>
+                                                                        <array>
+                                                                                <string>mon</string>
+                                                                                <string>tue</string>
+                                                                                <string>wed</string>
+                                                                                <string>thu</string>
+                                                                                <string>fri</string>
+                                                                        </array>
+
+                                                                        <!-- The earliest hour of a day at which an event might be scheduled. -->
+                                                                        <key>beginHour</key>
+                                                                        <integer>8</integer>
+
+                                                                        <!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). -->
+                                                                        <key>endHour</key>
+                                                                        <integer>16</integer>
+
+                                                                        <!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) -->
+                                                                        <key>tzname</key>
+                                                                        <string>America/Los_Angeles</string>
+                                                                </dict>
+                                                        </dict>
+                                                </dict>
+                                        </dict>
+
+                                </array>
+
+                                <!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. -->
+                                <key>weight</key>
+                                <integer>1</integer>
+                        </dict>
+                </array>
+        </dict>
+</plist>
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformanceloadtesttest_simpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/test_sim.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/test_sim.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/loadtest/test_sim.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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"> "weight": 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, {"foo": "bar"}, [ProfileType(Eventer, {
</span><span class="lines">@@ -495,7 +497,9 @@
</span><span class="cx"> """
</span><span class="cx"> config = FilePath(self.mktemp())
</span><span class="cx"> config.setContent(writePlistToString({"clients": []}))
</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"> "/principals/users/%s/",
</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="CalendarServerbranchesuserscdaboofixnoischedulecontribperformancesqlusagerequestshttpTestspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/requests/httpTests.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/requests/httpTests.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/requests/httpTests.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -89,12 +89,21 @@
</span><span class="cx"> pos = line.find(": ")
</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] == "*** SQL Stats ***":
+ if lines[offset + 2].split()[1] != "unlabeled":
+ 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("%s-%d-%s" % (self.logFilePath, event_count, self.label), "w") as f:
</span><span class="cx"> f.write(data)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribperformancesqlusagesqlusagepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/sqlusage.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/sqlusage.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/performance/sqlusage/sqlusage.py        2013-11-01 22:25:30 UTC (rev 11871)
</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(" Test = %s" % (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="CalendarServerbranchesuserscdaboofixnoischedulecontribtoolsfix_calendar"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/fix_calendar (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/fix_calendar        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/fix_calendar        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -28,9 +28,9 @@
</span><span class="cx"> def usage():
</span><span class="cx"> print """Usage: xattr_fix CALENDARS
</span><span class="cx"> Options:
</span><del>-
</del><ins>+
</ins><span class="cx"> CALENDARS - a list of directories that are to be treated as calendars
</span><del>-
</del><ins>+
</ins><span class="cx"> Description:
</span><span class="cx"> This utility will add xattrs to the specified directories and their contents
</span><span class="cx"> to make them appear to be calendars and calendar resources when used with
</span><span class="lines">@@ -40,8 +40,10 @@
</span><span class="cx"> root without properly preserving the xattrs.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def fixCalendar(path):
</span><del>-
</del><ins>+
</ins><span class="cx"> # First fix the resourcetype & getctag on the calendar
</span><span class="cx"> x = xattr.xattr(path)
</span><span class="cx"> x["WebDAV:{DAV:}resourcetype"] = """<?xml version='1.0' encoding='UTF-8'?>
</span><span class="lines">@@ -60,7 +62,7 @@
</span><span class="cx"> if not child.endswith(".ics"):
</span><span class="cx"> continue
</span><span class="cx"> fullpath = os.path.join(path, child)
</span><del>-
</del><ins>+
</ins><span class="cx"> # getcontenttype
</span><span class="cx"> x = xattr.xattr(fullpath)
</span><span class="cx"> x["WebDAV:{DAV:}getcontenttype"] = """<?xml version='1.0' encoding='UTF-8'?>
</span><span class="lines">@@ -94,7 +96,7 @@
</span><span class="cx"> if not os.path.exists(arg):
</span><span class="cx"> print "Path does not exist: '%s'. Ignoring." % (arg,)
</span><span class="cx"> continue
</span><del>-
</del><ins>+
</ins><span class="cx"> if os.path.basename(arg) in ("inbox", "outbox", "dropbox",):
</span><span class="cx"> print "Cannot be used on inbox, outbox or dropbox."
</span><span class="cx"> continue
</span><span class="lines">@@ -103,4 +105,3 @@
</span><span class="cx">
</span><span class="cx"> except Exception, e:
</span><span class="cx"> sys.exit(str(e))
</span><del>-
</del><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulecontribtoolsprotocolanalysispy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/protocolanalysis.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/protocolanalysis.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/contrib/tools/protocolanalysis.py        2013-11-01 22:25:30 UTC (rev 11871)
</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[" TOTAL"][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("responses", 0))
</span><span class="lines">@@ -1029,7 +1048,10 @@
</span><span class="cx"> #print("User Response times")
</span><span class="cx"> #self.printUserResponseTimes(doTabs)
</span><span class="cx">
</span><ins>+ print("Sim values")
+ 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"> "Total:",
</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("")
</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("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ tables.Table.ColumnFormat("%s", tables.Table.ColumnFormat.RIGHT_JUSTIFY),
+ ))
+ table.addHeader(("Item", "Value", "Items, per User, per Day", "Interval (sec), per item, per user"))
+ table.addRow(("Unique Users", users, "", ""))
</ins><span class="cx">
</span><ins>+ def _addRow(title, item):
+ table.addRow((title, item, "%.1f" % (safePercent(24 * item, hours * users, 1.0),), "%.1f" % (safePercent(hours * 60 * 60 * users, item, 1.0),),))
+
+ _addRow("New Events", self.newEvents)
+ _addRow("New Invites", self.newInvites)
+ _addRow("Updated Events", self.updateEvents)
+ _addRow("Updated Invites", self.updateInvites)
+ _addRow("Attendee Invites", self.attendeeInvites)
+ table.addRow((
+ "Recipients",
+ "%.1f" % (safePercent(sum(self.averagedHourlyByRecipientCount["iTIP Average"]), self.timeCounts, 1.0),),
+ "",
+ "",
+ ))
+ table.printTabDelimitedData() if doTabs else table.printTable()
+ print("")
+
+
+
</ins><span class="cx"> class TablePrinter(object):
</span><span class="cx">
</span><span class="cx"> @classmethod
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulesupportbuildsh"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/build.sh (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/build.sh        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/build.sh        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -598,10 +598,11 @@
</span><span class="cx">
</span><span class="cx"> export PATH="${dstroot}/bin:${PATH}";
</span><span class="cx"> export C_INCLUDE_PATH="${dstroot}/include:${C_INCLUDE_PATH:-}";
</span><del>- export LD_LIBRARY_PATH="${dstroot}/lib:${LD_LIBRARY_PATH:-}";
</del><ins>+ export LD_LIBRARY_PATH="${dstroot}/lib:${dstroot}/lib64:${LD_LIBRARY_PATH:-}";
</ins><span class="cx"> export CPPFLAGS="-I${dstroot}/include ${CPPFLAGS:-} ";
</span><del>- export LDFLAGS="-L${dstroot}/lib ${LDFLAGS:-} ";
- export DYLD_LIBRARY_PATH="${dstroot}/lib:${DYLD_LIBRARY_PATH:-}";
</del><ins>+ export LDFLAGS="-L${dstroot}/lib -L${dstroot}/lib64 ${LDFLAGS:-} ";
+ export DYLD_LIBRARY_PATH="${dstroot}/lib:${dstroot}/lib64:${DYLD_LIBRARY_PATH:-}";
+ export PKG_CONFIG_PATH="${dstroot}/lib/pkgconfig:${PKG_CONFIG_PATH:-}";
</ins><span class="cx">
</span><span class="cx"> if "${do_setup}"; then
</span><span class="cx"> if "${force_setup}" || "${do_bundle}" || [ ! -d "${dstroot}" ]; then
</span><span class="lines">@@ -626,10 +627,10 @@
</span><span class="cx"> cat > "${dstroot}/environment.sh" << __EOF__
</span><span class="cx"> export PATH="${dstroot}/bin:\${PATH}";
</span><span class="cx"> export C_INCLUDE_PATH="${dstroot}/include:\${C_INCLUDE_PATH:-}";
</span><del>-export LD_LIBRARY_PATH="${dstroot}/lib:\${LD_LIBRARY_PATH:-}:\$ORACLE_HOME";
</del><ins>+export LD_LIBRARY_PATH="${dstroot}/lib:${dstroot}/lib64:\${LD_LIBRARY_PATH:-}:\$ORACLE_HOME";
</ins><span class="cx"> export CPPFLAGS="-I${dstroot}/include \${CPPFLAGS:-} ";
</span><del>-export LDFLAGS="-L${dstroot}/lib \${LDFLAGS:-} ";
-export DYLD_LIBRARY_PATH="${dstroot}/lib:\${DYLD_LIBRARY_PATH:-}:\$ORACLE_HOME";
</del><ins>+export LDFLAGS="-L${dstroot}/lib -L${dstroot}/lib64 \${LDFLAGS:-} ";
+export DYLD_LIBRARY_PATH="${dstroot}/lib:${dstroot}/lib64:\${DYLD_LIBRARY_PATH:-}:\$ORACLE_HOME";
</ins><span class="cx"> __EOF__
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -656,10 +657,10 @@
</span><span class="cx">
</span><span class="cx"> # Normally we depend on the system Python, but a bundle install should be as
</span><span class="cx"> # self-contained as possible.
</span><del>- local pyfn="Python-2.7.1";
- c_dependency -m "aa27bc25725137ba155910bd8e5ddc4f" \
</del><ins>+ local pyfn="Python-2.7.5";
+ c_dependency -m "6334b666b7ff2038c761d7b27ba699c1" \
</ins><span class="cx"> "Python" "${pyfn}" \
</span><del>- "http://www.python.org/ftp/python/2.7.1/${pyfn}.tar.bz2" \
</del><ins>+ "http://www.python.org/ftp/python/2.7.5/${pyfn}.tar.bz2" \
</ins><span class="cx"> --enable-shared;
</span><span class="cx"> # Be sure to use the Python we just built.
</span><span class="cx"> export PYTHON="$(type -p python)";
</span><span class="lines">@@ -707,6 +708,14 @@
</span><span class="cx"> --disable-bdb --disable-hdb;
</span><span class="cx"> fi;
</span><span class="cx">
</span><ins>+ if find_header ffi/ffi.h; then
+ using_system "libffi";
+ else
+ c_dependency -m "45f3b6dbc9ee7c7dfbbbc5feba571529" \
+ "libffi" "libffi-3.0.13" \
+ "ftp://sourceware.org/pub/libffi/libffi-3.0.13.tar.gz"
+ fi;
+
</ins><span class="cx"> #
</span><span class="cx"> # Python dependencies
</span><span class="cx"> #
</span><span class="lines">@@ -764,7 +773,7 @@
</span><span class="cx"> local v="4.1.1";
</span><span class="cx"> local n="PyGreSQL";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "71d0b8c5a382f635572eb52fee47cd08" -o \
</del><ins>+ py_dependency -v "${v}" -m "71d0b8c5a382f635572eb52fee47cd08" \
</ins><span class="cx"> "${n}" "pgdb" "${p}" \
</span><span class="cx"> "${pypi}/P/${n}/${p}.tgz";
</span><span class="cx">
</span><span class="lines">@@ -811,7 +820,7 @@
</span><span class="cx"> local v="0.1.2";
</span><span class="cx"> local n="sqlparse";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -v "${v}" -s "978874e5ebbd78e6d419e8182ce4fb3c30379642" \
</del><ins>+ py_dependency -v "${v}" -s "978874e5ebbd78e6d419e8182ce4fb3c30379642" \
</ins><span class="cx"> "SQLParse" "${n}" "${p}" \
</span><span class="cx"> "http://python-sqlparse.googlecode.com/files/${p}.tar.gz";
</span><span class="cx">
</span><span class="lines">@@ -821,7 +830,7 @@
</span><span class="cx"> local v="0.6.1";
</span><span class="cx"> local n="pyflakes";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -v "${v}" -m "00debd2280b962e915dfee552a675915" \
</del><ins>+ py_dependency -v "${v}" -m "00debd2280b962e915dfee552a675915" \
</ins><span class="cx"> "Pyflakes" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx"> fi;
</span><span class="lines">@@ -833,28 +842,28 @@
</span><span class="cx"> # Can't add "-v 2011g" to args because the version check expects numbers.
</span><span class="cx"> local n="pytz";
</span><span class="cx"> local p="${n}-2011n";
</span><del>- py_dependency -o -m "75ffdc113a4bcca8096ab953df746391" \
</del><ins>+ py_dependency -m "75ffdc113a4bcca8096ab953df746391" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="cx"> local v="2.5";
</span><span class="cx"> local n="pycrypto";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -v "${v}" -m "783e45d4a1a309e03ab378b00f97b291" \
</del><ins>+ py_dependency -v "${v}" -m "783e45d4a1a309e03ab378b00f97b291" \
</ins><span class="cx"> "PyCrypto" "${n}" "${p}" \
</span><span class="cx"> "http://ftp.dlitz.net/pub/dlitz/crypto/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="cx"> local v="0.1.2";
</span><span class="cx"> local n="pyasn1";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -v "${v}" -m "a7c67f5880a16a347a4d3ce445862a47" \
</del><ins>+ py_dependency -v "${v}" -m "a7c67f5880a16a347a4d3ce445862a47" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="cx"> local v="1.1.6";
</span><span class="cx"> local n="setproctitle";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -v "1.0" -m "1e42e43b440214b971f4b33c21eac369" \
</del><ins>+ py_dependency -v "1.0" -m "1e42e43b440214b971f4b33c21eac369" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/s/${n}/${p}.tar.gz";
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischedulesupportversionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/version.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/version.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/support/version.py        2013-11-01 22:25:30 UTC (rev 11871)
</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 = "5.1"
</del><ins>+ base_version = "5.2"
</ins><span class="cx">
</span><span class="cx"> branches = tuple(
</span><span class="cx"> branch.format(version=base_version)
</span><span class="lines">@@ -36,7 +36,7 @@
</span><span class="cx"> "trunk",
</span><span class="cx"> )
</span><span class="cx"> )
</span><del>-
</del><ins>+
</ins><span class="cx"> source_root = dirname(dirname(__file__))
</span><span class="cx">
</span><span class="cx"> for branch in branches:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletestserver"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/testserver (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/testserver        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/testserver        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -28,6 +28,8 @@
</span><span class="cx"> printres="";
</span><span class="cx"> subdir="";
</span><span class="cx"> random="--random";
</span><ins>+seed="";
+ssl="";
</ins><span class="cx">
</span><span class="cx"> usage ()
</span><span class="cx"> {
</span><span class="lines">@@ -40,13 +42,15 @@
</span><span class="cx"> echo " -r Print request and response";
</span><span class="cx"> echo " -s Set the serverinfo.xml";
</span><span class="cx"> echo " -t Set the CalDAVTester directory";
</span><ins>+ echo " -x Random seed to use.";
</ins><span class="cx"> echo " -v Verbose.";
</span><ins>+ echo " -z Use SSL.";
</ins><span class="cx">
</span><span class="cx"> if [ "${1-}" == "-" ]; then return 0; fi;
</span><span class="cx"> exit 64;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-while getopts 'hvrot:s:d:' option; do
</del><ins>+while getopts 'hvrozt:s:d:x:' option; do
</ins><span class="cx"> case "$option" in
</span><span class="cx"> '?') usage; ;;
</span><span class="cx"> 'h') usage -; exit 0; ;;
</span><span class="lines">@@ -56,6 +60,8 @@
</span><span class="cx"> 'r') printres="--always-print-request --always-print-response"; ;;
</span><span class="cx"> 'v') verbose="v"; ;;
</span><span class="cx"> 'o') random=""; ;;
</span><ins>+ 'x') seed="--random-seed ${OPTARG}"; ;;
+ 'z') ssl="--ssl"; ;;
</ins><span class="cx"> esac;
</span><span class="cx"> done;
</span><span class="cx">
</span><span class="lines">@@ -71,5 +77,5 @@
</span><span class="cx">
</span><span class="cx"> source "${wd}/support/shell.sh";
</span><span class="cx">
</span><del>-cd "${cdt}" && "${python}" testcaldav.py ${random} --print-details-onfail ${printres} -s "${serverinfo}" ${subdir} "$@";
</del><ins>+cd "${cdt}" && "${python}" testcaldav.py ${random} ${seed} ${ssl} --print-details-onfail ${printres} -s "${serverinfo}" ${subdir} "$@";
</ins><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextenterpriseadbapi2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/adbapi2.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/adbapi2.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/adbapi2.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -18,10 +18,10 @@
</span><span class="cx"> """
</span><span class="cx"> Asynchronous multi-process connection pool.
</span><span class="cx">
</span><del>-This is similar to L{twisted.enterprise.adbapi}, but can hold a transaction (and
-thereby a thread) open across multiple asynchronous operations, rather than
-forcing the transaction to be completed entirely in a thread and/or entirely in
-a single SQL statement.
</del><ins>+This is similar to L{twisted.enterprise.adbapi}, but can hold a transaction
+(and thereby a thread) open across multiple asynchronous operations, rather
+than forcing the transaction to be completed entirely in a thread and/or
+entirely in a single SQL statement.
</ins><span class="cx">
</span><span class="cx"> Also, this module includes an AMP protocol for multiplexing connections through
</span><span class="cx"> a single choke-point host. This is not currently in use, however, as AMP needs
</span><span class="lines">@@ -84,6 +84,15 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+def _destructively(aList):
+ """
+ Destructively iterate a list, popping elements from the beginning.
+ """
+ while aList:
+ yield aList.pop(0)
+
+
+
</ins><span class="cx"> def _deriveParameters(cursor, args):
</span><span class="cx"> """
</span><span class="cx"> Some DB-API extensions need to call special extension methods on
</span><span class="lines">@@ -118,6 +127,7 @@
</span><span class="cx"> return derived
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _deriveQueryEnded(cursor, derived):
</span><span class="cx"> """
</span><span class="cx"> A query which involved some L{IDerivedParameter}s just ended. Execute any
</span><span class="lines">@@ -142,6 +152,8 @@
</span><span class="cx"> """
</span><span class="cx"> implements(IAsyncTransaction)
</span><span class="cx">
</span><ins>+ noisy = False
+
</ins><span class="cx"> def __init__(self, pool, threadHolder, connection, cursor):
</span><span class="cx"> self._pool = pool
</span><span class="cx"> self._completed = "idle"
</span><span class="lines">@@ -169,33 +181,31 @@
</span><span class="cx"> """
</span><span class="cx"> Execute the given SQL on a thread, using a DB-API 2.0 cursor.
</span><span class="cx">
</span><del>- This method is invoked internally on a non-reactor thread, one dedicated
- to and associated with the current cursor. It executes the given SQL,
- re-connecting first if necessary, re-cycling the old connection if
- necessary, and then, if there are results from the statement (as
- determined by the DB-API 2.0 'description' attribute) it will fetch all
- the rows and return them, leaving them to be relayed to
</del><ins>+ This method is invoked internally on a non-reactor thread, one
+ dedicated to and associated with the current cursor. It executes the
+ given SQL, re-connecting first if necessary, re-cycling the old
+ connection if necessary, and then, if there are results from the
+ statement (as determined by the DB-API 2.0 'description' attribute) it
+ will fetch all the rows and return them, leaving them to be relayed to
</ins><span class="cx"> L{_ConnectedTxn.execSQL} via the L{ThreadHolder}.
</span><span class="cx">
</span><span class="cx"> The rules for possibly reconnecting automatically are: if this is the
</span><span class="cx"> very first statement being executed in this transaction, and an error
</span><span class="cx"> occurs in C{execute}, close the connection and try again. We will
</span><del>- ignore any errors from C{close()} (or C{rollback()}) and log them during
- this process. This is OK because adbapi2 always enforces transaction
- discipline: connections are never in autocommit mode, so if the first
- statement in a transaction fails, nothing can have happened to the
- database; as per the ADBAPI spec, a lost connection is a rolled-back
- transaction. In the cases where some databases fail to enforce
- transaction atomicity (i.e. schema manipulations), re-executing the same
- statement will result, at worst, in a spurious and harmless error (like
- "table already exists"), not corruption.
</del><ins>+ ignore any errors from C{close()} (or C{rollback()}) and log them
+ during this process. This is OK because adbapi2 always enforces
+ transaction discipline: connections are never in autocommit mode, so if
+ the first statement in a transaction fails, nothing can have happened
+ to the database; as per the ADBAPI spec, a lost connection is a
+ rolled-back transaction. In the cases where some databases fail to
+ enforce transaction atomicity (i.e. schema manipulations),
+ re-executing the same statement will result, at worst, in a spurious
+ and harmless error (like "table already exists"), not corruption.
</ins><span class="cx">
</span><span class="cx"> @param sql: The SQL string to execute.
</span><del>-
</del><span class="cx"> @type sql: C{str}
</span><span class="cx">
</span><span class="cx"> @param args: The bind parameters to pass to adbapi, if any.
</span><del>-
</del><span class="cx"> @type args: C{list} or C{None}
</span><span class="cx">
</span><span class="cx"> @param raiseOnZeroRowCount: If specified, an exception to raise when no
</span><span class="lines">@@ -203,7 +213,6 @@
</span><span class="cx">
</span><span class="cx"> @return: all the rows that resulted from execution of the given C{sql},
</span><span class="cx"> or C{None}, if the statement is one which does not produce results.
</span><del>-
</del><span class="cx"> @rtype: C{list} of C{tuple}, or C{NoneType}
</span><span class="cx">
</span><span class="cx"> @raise Exception: this function may raise any exception raised by the
</span><span class="lines">@@ -234,9 +243,9 @@
</span><span class="cx"> # happen in the transaction, then the connection has probably gone
</span><span class="cx"> # bad in the meanwhile, and we should try again.
</span><span class="cx"> if wasFirst:
</span><del>- # Report the error before doing anything else, since doing other
- # things may cause the traceback stack to be eliminated if they
- # raise exceptions (even internally).
</del><ins>+ # Report the error before doing anything else, since doing
+ # other things may cause the traceback stack to be eliminated
+ # if they raise exceptions (even internally).
</ins><span class="cx"> log.err(
</span><span class="cx"> Failure(),
</span><span class="cx"> "Exception from execute() on first statement in "
</span><span class="lines">@@ -292,11 +301,9 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx">
</span><del>- noisy = False
-
</del><span class="cx"> def execSQL(self, *args, **kw):
</span><span class="cx"> result = self._holder.submit(
</span><del>- lambda : self._reallyExecSQL(*args, **kw)
</del><ins>+ lambda: self._reallyExecSQL(*args, **kw)
</ins><span class="cx"> )
</span><span class="cx"> if self.noisy:
</span><span class="cx"> def reportResult(results):
</span><span class="lines">@@ -305,7 +312,7 @@
</span><span class="cx"> "SQL: %r %r" % (args, kw),
</span><span class="cx"> "Results: %r" % (results,),
</span><span class="cx"> "",
</span><del>- ]))
</del><ins>+ ]))
</ins><span class="cx"> return results
</span><span class="cx"> result.addBoth(reportResult)
</span><span class="cx"> return result
</span><span class="lines">@@ -328,8 +335,8 @@
</span><span class="cx"> self._completed = "ended"
</span><span class="cx"> def reallySomething():
</span><span class="cx"> """
</span><del>- Do the database work and set appropriate flags. Executed in the
- cursor thread.
</del><ins>+ Do the database work and set appropriate flags. Executed in
+ the cursor thread.
</ins><span class="cx"> """
</span><span class="cx"> if self._cursor is None or self._first:
</span><span class="cx"> return
</span><span class="lines">@@ -384,8 +391,8 @@
</span><span class="cx"> class _NoTxn(object):
</span><span class="cx"> """
</span><span class="cx"> An L{IAsyncTransaction} that indicates a local failure before we could even
</span><del>- communicate any statements (or possibly even any connection attempts) to the
- server.
</del><ins>+ communicate any statements (or possibly even any connection attempts) to
+ the server.
</ins><span class="cx"> """
</span><span class="cx"> implements(IAsyncTransaction)
</span><span class="cx">
</span><span class="lines">@@ -401,7 +408,6 @@
</span><span class="cx"> """
</span><span class="cx"> return fail(ConnectionError(self.reason))
</span><span class="cx">
</span><del>-
</del><span class="cx"> execSQL = _everything
</span><span class="cx"> commit = _everything
</span><span class="cx"> abort = _everything
</span><span class="lines">@@ -411,9 +417,9 @@
</span><span class="cx"> class _WaitingTxn(object):
</span><span class="cx"> """
</span><span class="cx"> A L{_WaitingTxn} is an implementation of L{IAsyncTransaction} which cannot
</span><del>- yet actually execute anything, so it waits and spools SQL requests for later
- execution. When a L{_ConnectedTxn} becomes available later, it can be
- unspooled onto that.
</del><ins>+ yet actually execute anything, so it waits and spools SQL requests for
+ later execution. When a L{_ConnectedTxn} becomes available later, it can
+ be unspooled onto that.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> implements(IAsyncTransaction)
</span><span class="lines">@@ -442,8 +448,7 @@
</span><span class="cx"> a Deferred to not interfere with the originally submitted order of
</span><span class="cx"> commands.
</span><span class="cx"> """
</span><del>- while self._spool:
- yield self._spool.pop(0)
</del><ins>+ return _destructively(self._spool)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _unspool(self, other):
</span><span class="lines">@@ -492,8 +497,9 @@
</span><span class="cx"> """
</span><span class="cx"> Callback for C{commit} and C{abort} Deferreds.
</span><span class="cx"> """
</span><del>- for operation in self._hooks:
</del><ins>+ for operation in _destructively(self._hooks):
</ins><span class="cx"> yield operation()
</span><ins>+ self.clear()
</ins><span class="cx"> returnValue(ignored)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -501,10 +507,19 @@
</span><span class="cx"> """
</span><span class="cx"> Implement L{IAsyncTransaction.postCommit}.
</span><span class="cx"> """
</span><del>- self._hooks.append(operation)
</del><ins>+ if self._hooks is not None:
+ self._hooks.append(operation)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def clear(self):
+ """
+ Remove all hooks from this operation. Once this is called, no
+ more hooks can be added ever again.
+ """
+ self._hooks = None
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class _CommitAndAbortHooks(object):
</span><span class="cx"> """
</span><span class="cx"> Shared implementation of post-commit and post-abort hooks.
</span><span class="lines">@@ -524,6 +539,7 @@
</span><span class="cx"> """
</span><span class="cx"> pre = self._preCommit.runHooks()
</span><span class="cx"> def ok(ignored):
</span><ins>+ self._abort.clear()
</ins><span class="cx"> return doCommit().addCallback(self._commit.runHooks)
</span><span class="cx"> def failed(why):
</span><span class="cx"> return self.abort().addCallback(lambda ignored: why)
</span><span class="lines">@@ -639,9 +655,9 @@
</span><span class="cx"> d = self._currentBlock._startExecuting()
</span><span class="cx"> d.addCallback(self._finishExecuting)
</span><span class="cx"> elif self._blockedQueue is not None:
</span><del>- # If there aren't any pending blocks any more, and there are spooled
- # statements that aren't part of a block, unspool all the statements
- # that have been held up until this point.
</del><ins>+ # If there aren't any pending blocks any more, and there are
+ # spooled statements that aren't part of a block, unspool all the
+ # statements that have been held up until this point.
</ins><span class="cx"> bq = self._blockedQueue
</span><span class="cx"> self._blockedQueue = None
</span><span class="cx"> bq._unspool(self)
</span><span class="lines">@@ -649,8 +665,8 @@
</span><span class="cx">
</span><span class="cx"> def _finishExecuting(self, result):
</span><span class="cx"> """
</span><del>- The active block just finished executing. Clear it and see if there are
- more blocks to execute, or if all the blocks are done and we should
</del><ins>+ The active block just finished executing. Clear it and see if there
+ are more blocks to execute, or if all the blocks are done and we should
</ins><span class="cx"> execute any queued free statements.
</span><span class="cx"> """
</span><span class="cx"> self._currentBlock = None
</span><span class="lines">@@ -659,8 +675,9 @@
</span><span class="cx">
</span><span class="cx"> def commit(self):
</span><span class="cx"> if self._blockedQueue is not None:
</span><del>- # We're in the process of executing a block of commands. Wait until
- # they're done. (Commit will be repeated in _checkNextBlock.)
</del><ins>+ # We're in the process of executing a block of commands. Wait
+ # until they're done. (Commit will be repeated in
+ # _checkNextBlock.)
</ins><span class="cx"> return self._blockedQueue.commit()
</span><span class="cx"> def reallyCommit():
</span><span class="cx"> self._markComplete()
</span><span class="lines">@@ -670,6 +687,8 @@
</span><span class="cx">
</span><span class="cx"> def abort(self):
</span><span class="cx"> self._markComplete()
</span><ins>+ self._commit.clear()
+ self._preCommit.clear()
</ins><span class="cx"> result = super(_SingleTxn, self).abort()
</span><span class="cx"> if self in self._pool._waiting:
</span><span class="cx"> self._stopWaiting()
</span><span class="lines">@@ -785,9 +804,9 @@
</span><span class="cx">
</span><span class="cx"> @param raiseOnZeroRowCount: see L{IAsyncTransaction.execSQL}
</span><span class="cx">
</span><del>- @param track: an internal parameter; was this called by application code
- or as part of unspooling some previously-queued requests? True if
- application code, False if unspooling.
</del><ins>+ @param track: an internal parameter; was this called by application
+ code or as part of unspooling some previously-queued requests?
+ True if application code, False if unspooling.
</ins><span class="cx"> """
</span><span class="cx"> if track and self._ended:
</span><span class="cx"> raise AlreadyFinishedError()
</span><span class="lines">@@ -970,8 +989,8 @@
</span><span class="cx"> super(ConnectionPool, self).stopService()
</span><span class="cx"> self._stopping = True
</span><span class="cx">
</span><del>- # Phase 1: Cancel any transactions that are waiting so they won't try to
- # eagerly acquire new connections as they flow into the free-list.
</del><ins>+ # Phase 1: Cancel any transactions that are waiting so they won't try
+ # to eagerly acquire new connections as they flow into the free-list.
</ins><span class="cx"> while self._waiting:
</span><span class="cx"> waiting = self._waiting[0]
</span><span class="cx"> waiting._stopWaiting()
</span><span class="lines">@@ -991,10 +1010,10 @@
</span><span class="cx"> # ThreadHolders.
</span><span class="cx"> while self._free:
</span><span class="cx"> # Releasing a L{_ConnectedTxn} doesn't automatically recycle it /
</span><del>- # remove it the way aborting a _SingleTxn does, so we need to .pop()
- # here. L{_ConnectedTxn.stop} really shouldn't be able to fail, as
- # it's just stopping the thread, and the holder's stop() is
- # independently submitted from .abort() / .close().
</del><ins>+ # remove it the way aborting a _SingleTxn does, so we need to
+ # .pop() here. L{_ConnectedTxn.stop} really shouldn't be able to
+ # fail, as it's just stopping the thread, and the holder's stop()
+ # is independently submitted from .abort() / .close().
</ins><span class="cx"> yield self._free.pop()._releaseConnection()
</span><span class="cx">
</span><span class="cx"> tp = self.reactor.getThreadPool()
</span><span class="lines">@@ -1011,8 +1030,8 @@
</span><span class="cx"> def connection(self, label="<unlabeled>"):
</span><span class="cx"> """
</span><span class="cx"> Find and immediately return an L{IAsyncTransaction} object. Execution
</span><del>- of statements, commit and abort on that transaction may be delayed until
- a real underlying database connection is available.
</del><ins>+ of statements, commit and abort on that transaction may be delayed
+ until a real underlying database connection is available.
</ins><span class="cx">
</span><span class="cx"> @return: an L{IAsyncTransaction}
</span><span class="cx"> """
</span><span class="lines">@@ -1158,6 +1177,7 @@
</span><span class="cx"> def toString(self, inObject):
</span><span class="cx"> return dumps(inObject)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def fromString(self, inString):
</span><span class="cx"> return loads(inString)
</span><span class="cx">
</span><span class="lines">@@ -1193,8 +1213,7 @@
</span><span class="cx"> if f.type in command.errors:
</span><span class="cx"> returnValue(f)
</span><span class="cx"> else:
</span><del>- log.err(Failure(),
- "shared database connection pool encountered error")
</del><ins>+ log.err(Failure(), "shared database connection pool error")
</ins><span class="cx"> raise FailsafeException()
</span><span class="cx"> else:
</span><span class="cx"> returnValue(val)
</span><span class="lines">@@ -1286,6 +1305,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ConnectionPoolConnection(AMP):
</span><span class="cx"> """
</span><span class="cx"> A L{ConnectionPoolConnection} is a single connection to a
</span><span class="lines">@@ -1402,7 +1422,8 @@
</span><span class="cx"> A client which can execute SQL.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, dialect=POSTGRES_DIALECT, paramstyle=DEFAULT_PARAM_STYLE):
</del><ins>+ def __init__(self, dialect=POSTGRES_DIALECT,
+ paramstyle=DEFAULT_PARAM_STYLE):
</ins><span class="cx"> # See DEFAULT_PARAM_STYLE FIXME above.
</span><span class="cx"> super(ConnectionPoolClient, self).__init__()
</span><span class="cx"> self._nextID = count().next
</span><span class="lines">@@ -1428,8 +1449,8 @@
</span><span class="cx"> """
</span><span class="cx"> Create a new networked provider of L{IAsyncTransaction}.
</span><span class="cx">
</span><del>- (This will ultimately call L{ConnectionPool.connection} on the other end
- of the wire.)
</del><ins>+ (This will ultimately call L{ConnectionPool.connection} on the other
+ end of the wire.)
</ins><span class="cx">
</span><span class="cx"> @rtype: L{IAsyncTransaction}
</span><span class="cx"> """
</span><span class="lines">@@ -1478,12 +1499,12 @@
</span><span class="cx"> @param derived: either C{None} or a C{list} of L{IDerivedParameter}
</span><span class="cx"> providers initially passed into the C{execSQL} that started this
</span><span class="cx"> query. The values of these object swill mutate the original input
</span><del>- parameters to resemble them. Although L{IDerivedParameter.preQuery}
- and L{IDerivedParameter.postQuery} are invoked on the other end of
- the wire, the local objects will be made to appear as though they
- were called here.
</del><ins>+ parameters to resemble them. Although
+ L{IDerivedParameter.preQuery} and L{IDerivedParameter.postQuery}
+ are invoked on the other end of the wire, the local objects will be
+ made to appear as though they were called here.
</ins><span class="cx">
</span><del>- @param noneResult: should the result of the query be C{None} (i.e. did
</del><ins>+ @param noneResult: should the result of the query be C{None} (i.e. did
</ins><span class="cx"> it not have a C{description} on the cursor).
</span><span class="cx"> """
</span><span class="cx"> if noneResult and not self.results:
</span><span class="lines">@@ -1492,8 +1513,8 @@
</span><span class="cx"> results = self.results
</span><span class="cx"> if derived is not None:
</span><span class="cx"> # 1) Bleecchh.
</span><del>- # 2) FIXME: add some direct tests in test_adbapi2, the unit test for
- # this crosses some abstraction boundaries so it's a little
</del><ins>+ # 2) FIXME: add some direct tests in test_adbapi2, the unit test
+ # for this crosses some abstraction boundaries so it's a little
</ins><span class="cx"> # integration-y and in the tests for twext.enterprise.dal
</span><span class="cx"> for remote, local in zip(derived, self._deriveDerived()):
</span><span class="cx"> local.__dict__ = remote.__dict__
</span><span class="lines">@@ -1519,8 +1540,8 @@
</span><span class="cx"> class _NetTransaction(_CommitAndAbortHooks):
</span><span class="cx"> """
</span><span class="cx"> A L{_NetTransaction} is an L{AMP}-protocol-based provider of the
</span><del>- L{IAsyncTransaction} interface. It sends SQL statements, query results, and
- commit/abort commands via an AMP socket to a pooling process.
</del><ins>+ L{IAsyncTransaction} interface. It sends SQL statements, query results,
+ and commit/abort commands via an AMP socket to a pooling process.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> implements(IAsyncTransaction)
</span><span class="lines">@@ -1562,7 +1583,8 @@
</span><span class="cx"> args = []
</span><span class="cx"> client = self._client
</span><span class="cx"> queryID = str(client._nextID())
</span><del>- query = client._queries[queryID] = _Query(sql, raiseOnZeroRowCount, args)
</del><ins>+ query = client._queries[queryID] = _Query(sql, raiseOnZeroRowCount,
+ args)
</ins><span class="cx"> result = (
</span><span class="cx"> client.callRemote(
</span><span class="cx"> ExecSQL, queryID=queryID, sql=sql, args=args,
</span><span class="lines">@@ -1594,6 +1616,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def abort(self):
</span><ins>+ self._commit.clear()
+ self._preCommit.clear()
</ins><span class="cx"> return self._complete(Abort).addCallback(self._abort.runHooks)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1617,6 +1641,7 @@
</span><span class="cx"> self.abort().addErrback(shush)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class _NetCommandBlock(object):
</span><span class="cx"> """
</span><span class="cx"> Net command block.
</span><span class="lines">@@ -1650,10 +1675,10 @@
</span><span class="cx"> """
</span><span class="cx"> Execute some SQL on this command block.
</span><span class="cx"> """
</span><del>- if (self._ended or
- self._transaction._completed and
- not self._transaction._committing or
- self._transaction._committed):
</del><ins>+ if (
+ self._ended or self._transaction._completed and
+ not self._transaction._committing or self._transaction._committed
+ ):
</ins><span class="cx"> raise AlreadyFinishedError()
</span><span class="cx"> return self._transaction.execSQL(sql, args, raiseOnZeroRowCount,
</span><span class="cx"> self._blockID)
</span><span class="lines">@@ -1670,4 +1695,3 @@
</span><span class="cx"> EndBlock, blockID=self._blockID,
</span><span class="cx"> transactionID=self._transaction._transactionID
</span><span class="cx"> )
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisedalsyntaxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/syntax.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/syntax.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/syntax.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1686,7 +1686,46 @@
</span><span class="cx"> SQLFragment(' in %s mode' % (self.mode,)))
</span><span class="cx">
</span><span class="cx">
</span><ins>+class DatabaseLock(_LockingStatement):
+ """
+ An SQL exclusive session level advisory lock
+ """
</ins><span class="cx">
</span><ins>+ def _toSQL(self, queryGenerator):
+ assert(queryGenerator.dialect == POSTGRES_DIALECT)
+ return SQLFragment('select pg_advisory_lock(1)')
+
+
+ def on(self, txn, *a, **kw):
+ """
+ Override on() to only execute on Postgres
+ """
+ if txn.dialect == POSTGRES_DIALECT:
+ return super(DatabaseLock, self).on(txn, *a, **kw)
+
+ return succeed(None)
+
+
+class DatabaseUnlock(_LockingStatement):
+ """
+ An SQL exclusive session level advisory lock
+ """
+
+ def _toSQL(self, queryGenerator):
+ assert(queryGenerator.dialect == POSTGRES_DIALECT)
+ return SQLFragment('select pg_advisory_unlock(1)')
+
+
+ def on(self, txn, *a, **kw):
+ """
+ Override on() to only execute on Postgres
+ """
+ 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"> """
</span><span class="cx"> An SQL 'savepoint' statement.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisedaltesttest_sqlsyntaxpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/test/test_sqlsyntax.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/test/test_sqlsyntax.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/dal/test/test_sqlsyntax.py        2013-11-01 22:25:30 UTC (rev 11871)
</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("lock table FOO in exclusive mode"))
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_databaseLock(self):
+ """
+ L{DatabaseLock} generates a ('pg_advisory_lock') statement
+ """
+ self.assertEquals(DatabaseLock().toSQL(),
+ SQLFragment("select pg_advisory_lock(1)"))
+
+
+ def test_databaseUnlock(self):
+ """
+ L{DatabaseUnlock} generates a ('pg_advisory_unlock') statement
+ """
+ self.assertEquals(DatabaseUnlock().toSQL(),
+ SQLFragment("select pg_advisory_unlock(1)"))
+
+
</ins><span class="cx"> def test_savepoint(self):
</span><span class="cx"> """
</span><span class="cx"> L{Savepoint} generates a ('savepoint') statement.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextenterpriseienterprisepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/ienterprise.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/ienterprise.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/ienterprise.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -67,7 +67,6 @@
</span><span class="cx"> A copy of the 'paramstyle' attribute from a DB-API 2.0 module.
</span><span class="cx"> """)
</span><span class="cx">
</span><del>-
</del><span class="cx"> dialect = Attribute(
</span><span class="cx"> """
</span><span class="cx"> A copy of the 'dialect' attribute from the connection pool. One of the
</span><span class="lines">@@ -100,8 +99,8 @@
</span><span class="cx"> """
</span><span class="cx"> Asynchronous execution of SQL.
</span><span class="cx">
</span><del>- Note that there is no {begin()} method; if an L{IAsyncTransaction} exists at
- all, it is assumed to have been started.
</del><ins>+ Note that there is no C{begin()} method; if an L{IAsyncTransaction} exists
+ at all, it is assumed to have been started.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def commit():
</span><span class="lines">@@ -167,17 +166,18 @@
</span><span class="cx">
</span><span class="cx"> This is useful when using database-specific features such as
</span><span class="cx"> sub-transactions where order of execution is importnat, but where
</span><del>- application code may need to perform I/O to determine what SQL, exactly,
- it wants to execute. Consider this fairly contrived example for an
- imaginary database::
</del><ins>+ application code may need to perform I/O to determine what SQL,
+ exactly, it wants to execute. Consider this fairly contrived example
+ for an imaginary database::
</ins><span class="cx">
</span><span class="cx"> def storeWebPage(url, block):
</span><span class="cx"> block.execSQL("BEGIN SUB TRANSACTION")
</span><span class="cx"> got = getPage(url)
</span><span class="cx"> def gotPage(data):
</span><del>- block.execSQL("INSERT INTO PAGES (TEXT) VALUES (?)", [data])
</del><ins>+ block.execSQL("INSERT INTO PAGES (TEXT) VALUES (?)",
+ [data])
</ins><span class="cx"> block.execSQL("INSERT INTO INDEX (TOKENS) VALUES (?)",
</span><del>- [tokenize(data)])
</del><ins>+ [tokenize(data)])
</ins><span class="cx"> lastStmt = block.execSQL("END SUB TRANSACTION")
</span><span class="cx"> block.end()
</span><span class="cx"> return lastStmt
</span><span class="lines">@@ -187,12 +187,12 @@
</span><span class="cx"> lambda x: txn.commit(), lambda f: txn.abort()
</span><span class="cx"> )
</span><span class="cx">
</span><del>- This fires off all the C{getPage} requests in parallel, and prepares all
- the necessary SQL immediately as the results arrive, but executes those
- statements in order. In the above example, this makes sure to store the
- page and its tokens together, another use for this might be to store a
- computed aggregate (such as a sum) at a particular point in a
- transaction, without sacrificing parallelism.
</del><ins>+ This fires off all the C{getPage} requests in parallel, and prepares
+ all the necessary SQL immediately as the results arrive, but executes
+ those statements in order. In the above example, this makes sure to
+ store the page and its tokens together, another use for this might be
+ to store a computed aggregate (such as a sum) at a particular point in
+ a transaction, without sacrificing parallelism.
</ins><span class="cx">
</span><span class="cx"> @rtype: L{ICommandBlock}
</span><span class="cx"> """
</span><span class="lines">@@ -208,21 +208,21 @@
</span><span class="cx">
</span><span class="cx"> def end():
</span><span class="cx"> """
</span><del>- End this command block, allowing other commands queued on the underlying
- transaction to end.
</del><ins>+ End this command block, allowing other commands queued on the
+ underlying transaction to end.
</ins><span class="cx">
</span><span class="cx"> @note: This is I{not} the same as either L{IAsyncTransaction.commit} or
</span><span class="cx"> L{IAsyncTransaction.abort}, since it does not denote success or
</span><span class="cx"> failure; merely that the command block has completed and other
</span><span class="cx"> statements may now be executed. Since sub-transactions are a
</span><span class="cx"> database-specific feature, they must be implemented at a
</span><del>- higher-level than this facility provides (although this facility may
- be useful in their implementation). Also note that, unlike either
- of those methods, this does I{not} return a Deferred: if you want to
- know when the block has completed, simply add a callback to the last
- L{ICommandBlock.execSQL} call executed on this L{ICommandBlock}.
- (This may be changed in a future version for the sake of
- convenience, however.)
</del><ins>+ higher-level than this facility provides (although this facility
+ may be useful in their implementation). Also note that, unlike
+ either of those methods, this does I{not} return a Deferred: if you
+ want to know when the block has completed, simply add a callback to
+ the last L{ICommandBlock.execSQL} call executed on this
+ L{ICommandBlock}. (This may be changed in a future version for the
+ sake of convenience, however.)
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -306,7 +306,8 @@
</span><span class="cx"> L{WorkProposal}
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def transferProposalCallbacks(self, newQueuer):
</span><span class="cx"> """
</span><span class="cx"> Transfer the registered callbacks to the new queuer.
</span><del>- """
</del><span class="cx">\ No newline at end of file
</span><ins>+ """
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextenterprisetesttest_adbapi2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/test/test_adbapi2.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/test/test_adbapi2.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/enterprise/test/test_adbapi2.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -18,13 +18,15 @@
</span><span class="cx"> Tests for L{twext.enterprise.adbapi2}.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+import gc
+
</ins><span class="cx"> from zope.interface.verify import verifyObject
</span><span class="cx">
</span><span class="cx"> from twisted.python.failure import Failure
</span><span class="cx">
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx">
</span><del>-from twisted.internet.defer import Deferred, fail
</del><ins>+from twisted.internet.defer import Deferred, fail, succeed, inlineCallbacks
</ins><span class="cx">
</span><span class="cx"> from twisted.test.proto_helpers import StringTransport
</span><span class="cx">
</span><span class="lines">@@ -43,7 +45,37 @@
</span><span class="cx"> from twext.enterprise.fixtures import RollbackFail
</span><span class="cx"> from twext.enterprise.fixtures import CommitFail
</span><span class="cx"> from twext.enterprise.adbapi2 import Commit
</span><ins>+from twext.enterprise.adbapi2 import _HookableOperation
</ins><span class="cx">
</span><ins>+
+class TrashCollector(object):
+ """
+ Test helper for monitoring gc.garbage.
+ """
+ def __init__(self, testCase):
+ self.testCase = testCase
+ testCase.addCleanup(self.checkTrash)
+ self.start()
+
+
+ def start(self):
+ gc.collect()
+ self.garbageStart = len(gc.garbage)
+
+
+ def checkTrash(self):
+ """
+ Ensure that the test has added no additional garbage.
+ """
+ gc.collect()
+ newGarbage = gc.garbage[self.garbageStart:]
+ if newGarbage:
+ # Don't clean up twice.
+ self.start()
+ self.testCase.fail("New garbage: " + repr(newGarbage))
+
+
+
</ins><span class="cx"> class AssertResultHelper(object):
</span><span class="cx"> """
</span><span class="cx"> Mixin for asserting about synchronous Deferred results.
</span><span class="lines">@@ -300,8 +332,8 @@
</span><span class="cx"> def test_stopServiceWithSpooled(self):
</span><span class="cx"> """
</span><span class="cx"> When L{ConnectionPool.stopService} is called when spooled transactions
</span><del>- are outstanding, any pending L{Deferreds} returned by those transactions
- will be failed with L{ConnectionError}.
</del><ins>+ are outstanding, any pending L{Deferreds} returned by those
+ transactions will be failed with L{ConnectionError}.
</ins><span class="cx"> """
</span><span class="cx"> # Use up the free slots so we have to spool.
</span><span class="cx"> hold = []
</span><span class="lines">@@ -450,7 +482,8 @@
</span><span class="cx"> stopResult = self.resultOf(self.pool.stopService())
</span><span class="cx"> # Sanity check that we haven't actually stopped it yet
</span><span class="cx"> self.assertEquals(abortResult, [])
</span><del>- # We haven't fired it yet, so the service had better not have stopped...
</del><ins>+ # We haven't fired it yet, so the service had better not have
+ # stopped...
</ins><span class="cx"> self.assertEquals(stopResult, [])
</span><span class="cx"> d.callback(None)
</span><span class="cx"> self.flushHolders()
</span><span class="lines">@@ -465,7 +498,6 @@
</span><span class="cx"> """
</span><span class="cx"> t = self.createTransaction()
</span><span class="cx"> self.resultOf(t.execSQL("echo", []))
</span><del>- import gc
</del><span class="cx"> conns = self.factory.connections
</span><span class="cx"> self.assertEquals(len(conns), 1)
</span><span class="cx"> self.assertEquals(conns[0]._rollbackCount, 0)
</span><span class="lines">@@ -477,6 +509,60 @@
</span><span class="cx"> self.assertEquals(conns[0]._commitCount, 0)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def circularReferenceTest(self, finish, hook):
+ """
+ Collecting a completed (committed or aborted) L{IAsyncTransaction}
+ should not leak any circular references.
+ """
+ tc = TrashCollector(self)
+ commitExecuted = []
+ def carefullyManagedScope():
+ t = self.createTransaction()
+ def holdAReference():
+ """
+ This is a hook that holds a reference to 't'.
+ """
+ commitExecuted.append(True)
+ return t.execSQL("teardown", [])
+ hook(t, holdAReference)
+ finish(t)
+ self.failIf(commitExecuted, "Commit hook executed.")
+ carefullyManagedScope()
+ tc.checkTrash()
+
+
+ def test_noGarbageOnCommit(self):
+ """
+ Committing a transaction does not cause gc garbage.
+ """
+ self.circularReferenceTest(lambda txn: txn.commit(),
+ lambda txn, hook: txn.preCommit(hook))
+
+
+ def test_noGarbageOnCommitWithAbortHook(self):
+ """
+ Committing a transaction does not cause gc garbage.
+ """
+ self.circularReferenceTest(lambda txn: txn.commit(),
+ lambda txn, hook: txn.postAbort(hook))
+
+
+ def test_noGarbageOnAbort(self):
+ """
+ Aborting a transaction does not cause gc garbage.
+ """
+ self.circularReferenceTest(lambda txn: txn.abort(),
+ lambda txn, hook: txn.preCommit(hook))
+
+
+ def test_noGarbageOnAbortWithPostCommitHook(self):
+ """
+ Aborting a transaction does not cause gc garbage.
+ """
+ self.circularReferenceTest(lambda txn: txn.abort(),
+ lambda txn, hook: txn.postCommit(hook))
+
+
</ins><span class="cx"> def test_tooManyConnectionsWhileOthersFinish(self):
</span><span class="cx"> """
</span><span class="cx"> L{ConnectionPool.connection} will not spawn more than the maximum
</span><span class="lines">@@ -553,10 +639,11 @@
</span><span class="cx">
</span><span class="cx"> def test_reConnectWhenFirstExecFails(self):
</span><span class="cx"> """
</span><del>- Generally speaking, DB-API 2.0 adapters do not provide information about
- the cause of a failed 'execute' method; they definitely don't provide it
- in a way which can be identified as related to the syntax of the query,
- the state of the database itself, the state of the connection, etc.
</del><ins>+ Generally speaking, DB-API 2.0 adapters do not provide information
+ about the cause of a failed 'execute' method; they definitely don't
+ provide it in a way which can be identified as related to the syntax of
+ the query, the state of the database itself, the state of the
+ connection, etc.
</ins><span class="cx">
</span><span class="cx"> Therefore the best general heuristic for whether the connection to the
</span><span class="cx"> database has been lost and needs to be re-established is to catch
</span><span class="lines">@@ -564,8 +651,8 @@
</span><span class="cx"> transaction.
</span><span class="cx"> """
</span><span class="cx"> # Allow 'connect' to succeed. This should behave basically the same
</span><del>- # whether connect() happened to succeed in some previous transaction and
- # it's recycling the underlying transaction, or connect() just
</del><ins>+ # whether connect() happened to succeed in some previous transaction
+ # and it's recycling the underlying transaction, or connect() just
</ins><span class="cx"> # succeeded. Either way you just have a _SingleTxn wrapping a
</span><span class="cx"> # _ConnectedTxn.
</span><span class="cx"> txn = self.createTransaction()
</span><span class="lines">@@ -636,8 +723,8 @@
</span><span class="cx"> """
</span><span class="cx"> class BindingSpecificException(Exception):
</span><span class="cx"> """
</span><del>- Exception that's a placeholder for something that a database binding
- might raise.
</del><ins>+ Exception that's a placeholder for something that a database
+ binding might raise.
</ins><span class="cx"> """
</span><span class="cx"> def alsoFailClose(factory):
</span><span class="cx"> factory.childCloseWillFail(BindingSpecificException())
</span><span class="lines">@@ -738,8 +825,8 @@
</span><span class="cx"> therefore pointless, and can be ignored. Furthermore, actually
</span><span class="cx"> executing the commit and propagating a possible connection-oriented
</span><span class="cx"> error causes clients to see errors, when, if those clients had actually
</span><del>- executed any statements, the connection would have been recycled and the
- statement transparently re-executed by the logic tested by
</del><ins>+ executed any statements, the connection would have been recycled and
+ the statement transparently re-executed by the logic tested by
</ins><span class="cx"> L{test_reConnectWhenFirstExecFails}.
</span><span class="cx"> """
</span><span class="cx"> txn = self.createTransaction()
</span><span class="lines">@@ -758,12 +845,12 @@
</span><span class="cx">
</span><span class="cx"> def test_reConnectWhenSecondExecFailsThenFirstExecFails(self):
</span><span class="cx"> """
</span><del>- Other connection-oriented errors might raise exceptions if they occur in
- the middle of a transaction, but that should cause the error to be
- caught, the transaction to be aborted, and the (closed) connection to be
- recycled, where the next transaction that attempts to do anything with
- it will encounter the error immediately and discover it needs to be
- recycled.
</del><ins>+ Other connection-oriented errors might raise exceptions if they occur
+ in the middle of a transaction, but that should cause the error to be
+ caught, the transaction to be aborted, and the (closed) connection to
+ be recycled, where the next transaction that attempts to do anything
+ with it will encounter the error immediately and discover it needs to
+ be recycled.
</ins><span class="cx">
</span><span class="cx"> It would be better if this behavior were invisible, but that could only
</span><span class="cx"> be accomplished with more precise database exceptions. We may come up
</span><span class="lines">@@ -780,9 +867,9 @@
</span><span class="cx"> self.assertEquals(self.factory.connections[0].executions, 2)
</span><span class="cx"> # Reconnection should work exactly as before.
</span><span class="cx"> self.assertEquals(self.factory.connections[0].closed, False)
</span><del>- # Application code has to roll back its transaction at this point, since
- # it failed (and we don't necessarily know why it failed: not enough
- # information).
</del><ins>+ # Application code has to roll back its transaction at this point,
+ # since it failed (and we don't necessarily know why it failed: not
+ # enough information).
</ins><span class="cx"> self.resultOf(txn.abort())
</span><span class="cx"> self.factory.connections[0].executions = 0 # re-set for next test
</span><span class="cx"> self.assertEquals(len(self.factory.connections), 1)
</span><span class="lines">@@ -888,7 +975,7 @@
</span><span class="cx"> self.assertEquals(len(e), 1)
</span><span class="cx">
</span><span class="cx">
</span><del>- def test_twoCommandBlocks(self, flush=lambda : None):
</del><ins>+ def test_twoCommandBlocks(self, flush=lambda: None):
</ins><span class="cx"> """
</span><span class="cx"> When execution of one command block is complete, it will proceed to the
</span><span class="cx"> next queued block, then to regular SQL executed on the transaction.
</span><span class="lines">@@ -932,9 +1019,9 @@
</span><span class="cx"> def test_commandBlockDelaysCommit(self):
</span><span class="cx"> """
</span><span class="cx"> Some command blocks need to run asynchronously, without the overall
</span><del>- transaction-managing code knowing how far they've progressed. Therefore
- when you call {IAsyncTransaction.commit}(), it should not actually take
- effect if there are any pending command blocks.
</del><ins>+ transaction-managing code knowing how far they've progressed.
+ Therefore when you call {IAsyncTransaction.commit}(), it should not
+ actually take effect if there are any pending command blocks.
</ins><span class="cx"> """
</span><span class="cx"> txn = self.createTransaction()
</span><span class="cx"> block = txn.commandBlock()
</span><span class="lines">@@ -1078,8 +1165,8 @@
</span><span class="cx">
</span><span class="cx"> def pump(self):
</span><span class="cx"> """
</span><del>- Deliver all input from the client to the server, then from the server to
- the client.
</del><ins>+ Deliver all input from the client to the server, then from the server
+ to the client.
</ins><span class="cx"> """
</span><span class="cx"> a = self.moveData(self.c2s)
</span><span class="cx"> b = self.moveData(self.s2c)
</span><span class="lines">@@ -1187,3 +1274,31 @@
</span><span class="cx"> self.assertEquals(len(self.factory.connections), 1)
</span><span class="cx">
</span><span class="cx">
</span><ins>+class HookableOperationTests(TestCase):
+ """
+ Tests for L{_HookableOperation}.
+ """
+
+ @inlineCallbacks
+ def test_clearPreventsSubsequentAddHook(self):
+ """
+ After clear() or runHooks() are called, subsequent calls to addHook()
+ are NO-OPs.
+ """
+ def hook():
+ return succeed(None)
+
+ hookOp = _HookableOperation()
+ hookOp.addHook(hook)
+ self.assertEquals(len(hookOp._hooks), 1)
+ hookOp.clear()
+ self.assertEquals(hookOp._hooks, None)
+
+ hookOp = _HookableOperation()
+ hookOp.addHook(hook)
+ yield hookOp.runHooks()
+ self.assertEquals(hookOp._hooks, None)
+ hookOp.addHook(hook)
+ self.assertEquals(hookOp._hooks, None)
+
+
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextinternetsendfdportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/sendfdport.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/sendfdport.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/sendfdport.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -95,6 +95,7 @@
</span><span class="cx"> used to transmit sockets to a subprocess.
</span><span class="cx">
</span><span class="cx"> @ivar skt: the UNIX socket used as the sendmsg() transport.
</span><ins>+ @type skt: L{socket.socket}
</ins><span class="cx">
</span><span class="cx"> @ivar outgoingSocketQueue: an outgoing queue of sockets to send to the
</span><span class="cx"> subprocess, along with their descriptions (strings describing their
</span><span class="lines">@@ -107,7 +108,11 @@
</span><span class="cx"> from the subprocess: this is an application-specific indication of how
</span><span class="cx"> ready this subprocess is to receive more connections. A typical usage
</span><span class="cx"> would be to count the open connections: this is what is passed to
</span><del>- @type status: C{str}
</del><ins>+ @type status: See L{IStatusWatcher} for an explanation of which methods
+ determine this type.
+
+ @ivar dispatcher: The socket dispatcher that owns this L{_SubprocessSocket}
+ @type dispatcher: L{InheritedSocketDispatcher}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self, dispatcher, skt, status):
</span><span class="lines">@@ -117,6 +122,7 @@
</span><span class="cx"> self.skt = skt # XXX needs to be set non-blocking by somebody
</span><span class="cx"> self.fileno = skt.fileno
</span><span class="cx"> self.outgoingSocketQueue = []
</span><ins>+ self.pendingCloseSocketQueue = []
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sendSocketToPeer(self, skt, description):
</span><span class="lines">@@ -127,7 +133,7 @@
</span><span class="cx"> self.startWriting()
</span><span class="cx">
</span><span class="cx">
</span><del>- def doRead(self):
</del><ins>+ def doRead(self, recvmsg=recvmsg):
</ins><span class="cx"> """
</span><span class="cx"> Receive a status / health message and record it.
</span><span class="cx"> """
</span><span class="lines">@@ -137,10 +143,12 @@
</span><span class="cx"> if se.errno not in (EAGAIN, ENOBUFS):
</span><span class="cx"> raise
</span><span class="cx"> else:
</span><del>- self.dispatcher.statusMessage(self, data)
</del><ins>+ closeCount = self.dispatcher.statusMessage(self, data)
+ for ignored in xrange(closeCount):
+ self.pendingCloseSocketQueue.pop(0).close()
</ins><span class="cx">
</span><span class="cx">
</span><del>- def doWrite(self):
</del><ins>+ def doWrite(self, sendfd=sendfd):
</ins><span class="cx"> """
</span><span class="cx"> Transmit as many queued pending file descriptors as we can.
</span><span class="cx"> """
</span><span class="lines">@@ -153,6 +161,10 @@
</span><span class="cx"> self.outgoingSocketQueue.insert(0, (skt, desc))
</span><span class="cx"> return
</span><span class="cx"> raise
</span><ins>+
+ # Ready to close this socket; wait until it is acknowledged.
+ self.pendingCloseSocketQueue.append(skt)
+
</ins><span class="cx"> if not self.outgoingSocketQueue:
</span><span class="cx"> self.stopWriting()
</span><span class="cx">
</span><span class="lines">@@ -185,7 +197,7 @@
</span><span class="cx"> than the somewhat more abstract language that would be accurate.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def initialStatus():
</del><ins>+ def initialStatus(): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> A new socket was created and added to the dispatcher. Compute an
</span><span class="cx"> initial value for its status.
</span><span class="lines">@@ -194,7 +206,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><del>- def newConnectionStatus(previousStatus):
</del><ins>+ def newConnectionStatus(previousStatus): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> A new connection was sent to a given socket. Compute its status based
</span><span class="cx"> on the previous status of that socket.
</span><span class="lines">@@ -206,7 +218,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><del>- def statusFromMessage(previousStatus, message):
</del><ins>+ def statusFromMessage(previousStatus, message): #@NoSelf
</ins><span class="cx"> """
</span><span class="cx"> A status message was received by a worker. Convert the previous status
</span><span class="cx"> value (returned from L{newConnectionStatus}, L{initialStatus}, or
</span><span class="lines">@@ -220,7 +232,18 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def closeCountFromStatus(previousStatus): #@NoSelf
+ """
+ Based on a status previously returned from a method on this
+ L{IStatusWatcher}, determine how many sockets may be closed.
</ins><span class="cx">
</span><ins>+ @return: a 2-tuple of C{number of sockets that may safely be closed},
+ C{new status}.
+ @rtype: 2-tuple of (C{int}, C{<opaque>})
+ """
+
+
+
</ins><span class="cx"> class InheritedSocketDispatcher(object):
</span><span class="cx"> """
</span><span class="cx"> Used by one or more L{InheritingProtocolFactory}s, this keeps track of a
</span><span class="lines">@@ -260,10 +283,11 @@
</span><span class="cx"> The status of a connection has changed; update all registered status
</span><span class="cx"> change listeners.
</span><span class="cx"> """
</span><del>- subsocket.status = self.statusWatcher.statusFromMessage(
- subsocket.status, message
- )
- self.statusWatcher.statusesChanged(self.statuses)
</del><ins>+ watcher = self.statusWatcher
+ status = watcher.statusFromMessage(subsocket.status, message)
+ closeCount, subsocket.status = watcher.closeCountFromStatus(status)
+ watcher.statusesChanged(self.statuses)
+ return closeCount
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sendFileDescriptor(self, skt, description):
</span><span class="lines">@@ -291,7 +315,7 @@
</span><span class="cx"> # XXX Maybe want to send along 'description' or 'skt' or some
</span><span class="cx"> # properties thereof? -glyph
</span><span class="cx"> selectedSocket.status = self.statusWatcher.newConnectionStatus(
</span><del>- selectedSocket.status
</del><ins>+ selectedSocket.status
</ins><span class="cx"> )
</span><span class="cx"> self.statusWatcher.statusesChanged(self.statuses)
</span><span class="cx">
</span><span class="lines">@@ -305,7 +329,7 @@
</span><span class="cx"> subSocket.startReading()
</span><span class="cx">
</span><span class="cx">
</span><del>- def addSocket(self):
</del><ins>+ def addSocket(self, socketpair=lambda: socketpair(AF_UNIX, SOCK_DGRAM)):
</ins><span class="cx"> """
</span><span class="cx"> Add a C{sendmsg()}-oriented AF_UNIX socket to the pool of sockets being
</span><span class="cx"> used for transmitting file descriptors to child processes.
</span><span class="lines">@@ -314,7 +338,7 @@
</span><span class="cx"> C{fileno()} as part of the C{childFDs} argument to
</span><span class="cx"> C{spawnProcess()}, then close it.
</span><span class="cx"> """
</span><del>- i, o = socketpair(AF_UNIX, SOCK_DGRAM)
</del><ins>+ i, o = socketpair()
</ins><span class="cx"> i.setblocking(False)
</span><span class="cx"> o.setblocking(False)
</span><span class="cx"> a = _SubprocessSocket(self, o, self.statusWatcher.initialStatus())
</span><span class="lines">@@ -412,4 +436,3 @@
</span><span class="cx"> """
</span><span class="cx"> self.statusQueue.append(statusMessage)
</span><span class="cx"> self.startWriting()
</span><del>-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextinternettesttest_sendfdportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/test/test_sendfdport.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/test/test_sendfdport.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/internet/test/test_sendfdport.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -23,14 +23,25 @@
</span><span class="cx"> import os
</span><span class="cx"> import fcntl
</span><span class="cx">
</span><ins>+from zope.interface.verify import verifyClass
+from zope.interface import implementer
+
</ins><span class="cx"> from twext.internet.sendfdport import InheritedSocketDispatcher
</span><span class="cx">
</span><span class="cx"> from twext.web2.metafd import ConnectionLimiter
</span><span class="cx"> from twisted.internet.interfaces import IReactorFDSet
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><del>-from zope.interface import implementer
</del><span class="cx">
</span><del>-@implementer(IReactorFDSet)
</del><ins>+def verifiedImplementer(interface):
+ def _(cls):
+ result = implementer(interface)(cls)
+ verifyClass(interface, result)
+ return result
+ return _
+
+
+
+@verifiedImplementer(IReactorFDSet)
</ins><span class="cx"> class ReaderAdder(object):
</span><span class="cx">
</span><span class="cx"> def __init__(self):
</span><span class="lines">@@ -50,7 +61,23 @@
</span><span class="cx"> self.writers.append(writer)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def removeAll(self):
+ self.__init__()
</ins><span class="cx">
</span><ins>+
+ def getWriters(self):
+ return self.writers[:]
+
+
+ def removeReader(self, reader):
+ self.readers.remove(reader)
+
+
+ def removeWriter(self, writer):
+ self.writers.remove(writer)
+
+
+
</ins><span class="cx"> def isNonBlocking(skt):
</span><span class="cx"> """
</span><span class="cx"> Determine if the given socket is blocking or not.
</span><span class="lines">@@ -66,22 +93,11 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-from zope.interface.verify import verifyClass
-from zope.interface import implementer
-
-def verifiedImplementer(interface):
- def _(cls):
- result = implementer(interface)(cls)
- verifyClass(interface, result)
- return result
- return _
-
-
-
</del><span class="cx"> @verifiedImplementer(IStatusWatcher)
</span><span class="cx"> class Watcher(object):
</span><span class="cx"> def __init__(self, q):
</span><span class="cx"> self.q = q
</span><ins>+ self._closeCounter = 1
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def newConnectionStatus(self, previous):
</span><span class="lines">@@ -100,7 +116,13 @@
</span><span class="cx"> return 0
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def closeCountFromStatus(self, status):
+ result = (self._closeCounter, status)
+ self._closeCounter += 1
+ return result
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class InheritedSocketDispatcherTests(TestCase):
</span><span class="cx"> """
</span><span class="cx"> Inherited socket dispatcher tests.
</span><span class="lines">@@ -110,6 +132,51 @@
</span><span class="cx"> self.dispatcher.reactor = ReaderAdder()
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_closeSomeSockets(self):
+ """
+ L{InheritedSocketDispatcher} determines how many sockets to close from
+ L{IStatusWatcher.closeCountFromStatus}.
+ """
+ self.dispatcher.statusWatcher = Watcher([])
+ class SocketForClosing(object):
+ blocking = True
+ closed = False
+ def setblocking(self, b):
+ self.blocking = b
+ def fileno(self):
+ return object()
+ def close(self):
+ self.closed = True
+
+ one = SocketForClosing()
+ two = SocketForClosing()
+ three = SocketForClosing()
+
+ self.dispatcher.addSocket(
+ lambda: (SocketForClosing(), SocketForClosing())
+ )
+
+ self.dispatcher.sendFileDescriptor(one, "one")
+ self.dispatcher.sendFileDescriptor(two, "two")
+ self.dispatcher.sendFileDescriptor(three, "three")
+ def sendfd(unixSocket, tcpSocket, description):
+ pass
+ # Put something into the socket-close queue.
+ self.dispatcher._subprocessSockets[0].doWrite(sendfd)
+ # Nothing closed yet.
+ self.assertEquals(one.closed, False)
+ self.assertEquals(two.closed, False)
+ self.assertEquals(three.closed, False)
+
+ def recvmsg(fileno):
+ return 'data', 0, 0
+ self.dispatcher._subprocessSockets[0].doRead(recvmsg)
+ # One socket closed.
+ self.assertEquals(one.closed, True)
+ self.assertEquals(two.closed, False)
+ self.assertEquals(three.closed, False)
+
+
</ins><span class="cx"> def test_nonBlocking(self):
</span><span class="cx"> """
</span><span class="cx"> Creating a L{_SubprocessSocket} via
</span><span class="lines">@@ -165,6 +232,7 @@
</span><span class="cx"> message = "whatever"
</span><span class="cx"> # Need to have a socket that will accept the descriptors.
</span><span class="cx"> dispatcher.addSocket()
</span><del>- dispatcher.statusMessage(dispatcher._subprocessSockets[0], message)
- dispatcher.statusMessage(dispatcher._subprocessSockets[0], message)
</del><ins>+ subskt = dispatcher._subprocessSockets[0]
+ dispatcher.statusMessage(subskt, message)
+ dispatcher.statusMessage(subskt, message)
</ins><span class="cx"> self.assertEquals(q, [[-1], [-2]])
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextpatchespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/patches.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/patches.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/patches.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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 > 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 "_resolveIPv6" in (getModule("twisted.internet.tcp")
</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"> """
</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 <http://tm.tl/5085>}
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextpythonlogpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/log.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/log.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/log.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -34,7 +34,8 @@
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="cx"> def oops(self, data):
</span><del>- self.log.error("Oops! Invalid data from server: {data!r}", data=data)
</del><ins>+ self.log.error("Oops! Invalid data from server: {data!r}",
+ data=data)
</ins><span class="cx">
</span><span class="cx"> C{Logger}s have namespaces, for which logging can be configured independently.
</span><span class="cx"> Namespaces may be specified by passing in a C{namespace} argument to L{Logger}
</span><span class="lines">@@ -76,14 +77,16 @@
</span><span class="cx"> from zope.interface import Interface, implementer
</span><span class="cx"> from twisted.python.constants import NamedConstant, Names
</span><span class="cx"> from twisted.python.failure import Failure
</span><del>-from twisted.python.reflect import safe_str
</del><ins>+from twisted.python.reflect import safe_str, safe_repr
</ins><span class="cx"> import twisted.python.log
</span><span class="cx"> from twisted.python.log import msg as twistedLogMessage
</span><span class="cx"> from twisted.python.log import addObserver, removeObserver
</span><span class="cx"> from twisted.python.log import ILogObserver as ILegacyLogObserver
</span><span class="cx">
</span><ins>+OBSERVER_REMOVED = (
+ "Temporarily removing observer {observer} due to exception: {e}"
+)
</ins><span class="cx">
</span><del>-
</del><span class="cx"> #
</span><span class="cx"> # Log level definitions
</span><span class="cx"> #
</span><span class="lines">@@ -150,24 +153,27 @@
</span><span class="cx"> """
</span><span class="cx"> return cls._levelPriorities[constant]
</span><span class="cx">
</span><del>-LogLevel._levelPriorities = dict((constant, idx)
- for (idx, constant) in
- (enumerate(LogLevel.iterconstants())))
</del><span class="cx">
</span><ins>+LogLevel._levelPriorities = dict(
+ (constant, idx) for (idx, constant) in
+ (enumerate(LogLevel.iterconstants()))
+)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # Mappings to Python's logging module
</span><span class="cx"> #
</span><span class="cx"> pythonLogLevelMapping = {
</span><del>- LogLevel.debug : logging.DEBUG,
- LogLevel.info : logging.INFO,
- LogLevel.warn : logging.WARNING,
- LogLevel.error : logging.ERROR,
- #LogLevel.critical: logging.CRITICAL,
</del><ins>+ LogLevel.debug: logging.DEBUG,
+ LogLevel.info: logging.INFO,
+ LogLevel.warn: logging.WARNING,
+ LogLevel.error: logging.ERROR,
+ # LogLevel.critical: logging.CRITICAL,
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # Loggers
</span><span class="cx"> ##
</span><span class="lines">@@ -206,21 +212,20 @@
</span><span class="cx"> return formatWithCall(format, event)
</span><span class="cx">
</span><span class="cx"> except BaseException as e:
</span><del>- try:
- return formatUnformattableEvent(event, e)
- except:
- return u"MESSAGE LOST"
</del><ins>+ return formatUnformattableEvent(event, e)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def formatUnformattableEvent(event, error):
</span><span class="cx"> """
</span><del>- Formats an event as a L{unicode} that describes the event
- generically and a formatting error.
</del><ins>+ Formats an event as a L{unicode} that describes the event generically and a
+ formatting error.
</ins><span class="cx">
</span><span class="cx"> @param event: a logging event
</span><ins>+ @type dict: L{dict}
</ins><span class="cx">
</span><span class="cx"> @param error: the formatting error
</span><ins>+ @type error: L{Exception}
</ins><span class="cx">
</span><span class="cx"> @return: a L{unicode}
</span><span class="cx"> """
</span><span class="lines">@@ -229,35 +234,22 @@
</span><span class="cx"> u"Unable to format event {event!r}: {error}"
</span><span class="cx"> .format(event=event, error=error)
</span><span class="cx"> )
</span><del>- except BaseException as error:
- #
</del><ins>+ except BaseException:
</ins><span class="cx"> # Yikes, something really nasty happened.
</span><span class="cx"> #
</span><del>- # Try to recover as much formattable data as possible;
- # hopefully at least the namespace is sane, which will
- # help you find the offending logger.
- #
- try:
- items = []
</del><ins>+ # Try to recover as much formattable data as possible; hopefully at
+ # least the namespace is sane, which will help you find the offending
+ # logger.
+ failure = Failure()
</ins><span class="cx">
</span><del>- for key, value in event.items():
- try:
- items.append(u"{key!r} = ".format(key=key))
- except:
- items.append(u"<UNFORMATTABLE KEY> = ")
- try:
- items.append(u"{value!r}".format(value=value))
- except:
- items.append(u"<UNFORMATTABLE VALUE>")
</del><ins>+ text = ", ".join(" = ".join((safe_repr(key), safe_repr(value)))
+ for key, value in event.items())
</ins><span class="cx">
</span><del>- text = ", ".join(items)
- except:
- text = ""
-
</del><span class="cx"> return (
</span><del>- u"MESSAGE LOST: Unformattable object logged: {error}\n"
- u"Recoverable data: {text}"
- .format(text=text)
</del><ins>+ u"MESSAGE LOST: unformattable object logged: {error}\n"
+ u"Recoverable data: {text}\n"
+ u"Exception during formatting:\n{failure}"
+ .format(error=safe_repr(error), failure=failure, text=text)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -344,28 +336,24 @@
</span><span class="cx"> @param kwargs: additional keyword parameters to include with
</span><span class="cx"> the event.
</span><span class="cx"> """
</span><del>- if level not in LogLevel.iterconstants(): # FIXME: Updated Twisted supports 'in' on constants container
</del><ins>+ # FIXME: Updated Twisted supports 'in' on constants container
+ if level not in LogLevel.iterconstants():
</ins><span class="cx"> self.failure(
</span><span class="cx"> "Got invalid log level {invalidLevel!r} in {logger}.emit().",
</span><span class="cx"> Failure(InvalidLogLevelError(level)),
</span><del>- invalidLevel = level,
- logger = self,
</del><ins>+ invalidLevel=level,
+ logger=self,
</ins><span class="cx"> )
</span><span class="cx"> #level = LogLevel.error
</span><span class="cx"> # FIXME: continue to emit?
</span><span class="cx"> return
</span><span class="cx">
</span><del>- event = kwargs
- event.update(
- log_logger = self,
- log_level = level,
- log_namespace = self.namespace,
- log_source = self.source,
- log_format = format,
- log_time = time.time(),
</del><ins>+ kwargs.update(
+ log_logger=self, log_level=level, log_namespace=self.namespace,
+ log_source=self.source, log_format=format, log_time=time.time(),
</ins><span class="cx"> )
</span><span class="cx">
</span><del>- self.publisher(event)
</del><ins>+ self.publisher(kwargs)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def failure(self, format, failure=None, level=LogLevel.error, **kwargs):
</span><span class="lines">@@ -381,8 +369,9 @@
</span><span class="cx">
</span><span class="cx"> or::
</span><span class="cx">
</span><del>- d = deferred_frob(knob)
- d.addErrback(lambda f: log.failure, "While frobbing {knob}", f, knob=knob)
</del><ins>+ d = deferredFrob(knob)
+ d.addErrback(lambda f: log.failure, "While frobbing {knob}",
+ f, knob=knob)
</ins><span class="cx">
</span><span class="cx"> @param format: a message format using new-style (PEP 3101)
</span><span class="cx"> formatting. The logging event (which is a L{dict}) is
</span><span class="lines">@@ -397,7 +386,7 @@
</span><span class="cx"> event.
</span><span class="cx"> """
</span><span class="cx"> if failure is None:
</span><del>- failure=Failure()
</del><ins>+ failure = Failure()
</ins><span class="cx">
</span><span class="cx"> self.emit(level, format, log_failure=failure, **kwargs)
</span><span class="cx">
</span><span class="lines">@@ -410,10 +399,10 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self, logger=None):
</span><del>- if logger is not None:
</del><ins>+ if logger is None:
+ self.newStyleLogger = Logger(Logger._namespaceFromCallingContext())
+ else:
</ins><span class="cx"> self.newStyleLogger = logger
</span><del>- else:
- self.newStyleLogger = Logger(Logger._namespaceFromCallingContext())
</del><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __getattribute__(self, name):
</span><span class="lines">@@ -446,10 +435,12 @@
</span><span class="cx"> _stuff = Failure(_stuff)
</span><span class="cx">
</span><span class="cx"> if isinstance(_stuff, Failure):
</span><del>- self.newStyleLogger.emit(LogLevel.error, failure=_stuff, why=_why, isError=1, **kwargs)
</del><ins>+ self.newStyleLogger.emit(LogLevel.error, failure=_stuff, why=_why,
+ isError=1, **kwargs)
</ins><span class="cx"> else:
</span><span class="cx"> # We got called with an invalid _stuff.
</span><del>- self.newStyleLogger.emit(LogLevel.error, repr(_stuff), why=_why, isError=1, **kwargs)
</del><ins>+ self.newStyleLogger.emit(LogLevel.error, repr(_stuff), why=_why,
+ isError=1, **kwargs)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -475,13 +466,15 @@
</span><span class="cx">
</span><span class="cx"> setattr(Logger, level.name, log_emit)
</span><span class="cx">
</span><del>-for level in LogLevel.iterconstants():
- bindEmit(level)
</del><span class="cx">
</span><del>-del level
</del><span class="cx">
</span><ins>+def _bindLevels():
+ for level in LogLevel.iterconstants():
+ bindEmit(level)
</ins><span class="cx">
</span><ins>+_bindLevels()
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # Observers
</span><span class="cx"> #
</span><span class="lines">@@ -545,11 +538,11 @@
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><del>- def __call__(self, event):
</del><ins>+ def __call__(self, event):
</ins><span class="cx"> for observer in self.observers:
</span><span class="cx"> try:
</span><span class="cx"> observer(event)
</span><del>- except:
</del><ins>+ except BaseException as e:
</ins><span class="cx"> #
</span><span class="cx"> # We have to remove the offending observer because
</span><span class="cx"> # we're going to badmouth it to all of its friends
</span><span class="lines">@@ -558,8 +551,8 @@
</span><span class="cx"> #
</span><span class="cx"> self.removeObserver(observer)
</span><span class="cx"> try:
</span><del>- self.log.failure("Observer {observer} raised an exception; removing.", observer=observer)
- except:
</del><ins>+ self.log.failure(OBSERVER_REMOVED, observer=observer, e=e)
+ except BaseException:
</ins><span class="cx"> pass
</span><span class="cx"> finally:
</span><span class="cx"> self.addObserver(observer)
</span><span class="lines">@@ -639,6 +632,8 @@
</span><span class="cx"> """
</span><span class="cx"> L{ILogFilterPredicate} that filters out events with a log level
</span><span class="cx"> lower than the log level for the event's namespace.
</span><ins>+
+ Events that not not have a log level or namespace are also dropped.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def __init__(self):
</span><span class="lines">@@ -701,11 +696,15 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __call__(self, event):
</span><del>- level = event["log_level"]
- namespace = event["log_namespace"]
</del><ins>+ level = event.get("log_level", None)
+ namespace = event.get("log_namespace", None)
</ins><span class="cx">
</span><del>- if (LogLevel._priorityForLevel(level) <
- LogLevel._priorityForLevel(self.logLevelForNamespace(namespace))):
</del><ins>+ if (
+ level is None or
+ namespace is None or
+ LogLevel._priorityForLevel(level) <
+ LogLevel._priorityForLevel(self.logLevelForNamespace(namespace))
+ ):
</ins><span class="cx"> return PredicateResult.no
</span><span class="cx">
</span><span class="cx"> return PredicateResult.maybe
</span><span class="lines">@@ -725,8 +724,8 @@
</span><span class="cx"> """
</span><span class="cx"> self.legacyObserver = legacyObserver
</span><span class="cx">
</span><del>-
- def __call__(self, event):
</del><ins>+
+ def __call__(self, event):
</ins><span class="cx"> prefix = "[{log_namespace}#{log_level.name}] ".format(**event)
</span><span class="cx">
</span><span class="cx"> level = event["log_level"]
</span><span class="lines">@@ -756,7 +755,9 @@
</span><span class="cx"> if "log_failure" in event:
</span><span class="cx"> event["failure"] = event["log_failure"]
</span><span class="cx"> event["isError"] = 1
</span><del>- event["why"] = "{prefix}{message}".format(prefix=prefix, message=formatEvent(event))
</del><ins>+ event["why"] = "{prefix}{message}".format(
+ prefix=prefix, message=formatEvent(event)
+ )
</ins><span class="cx">
</span><span class="cx"> self.legacyObserver(**event)
</span><span class="cx">
</span><span class="lines">@@ -814,7 +815,8 @@
</span><span class="cx"> self.legacyLogObserver = LegacyLogObserver(twistedLogMessage)
</span><span class="cx"> self.filteredPublisher = LogPublisher(self.legacyLogObserver)
</span><span class="cx"> self.levels = LogLevelFilterPredicate()
</span><del>- self.filters = FilteringLogObserver(self.filteredPublisher, (self.levels,))
</del><ins>+ self.filters = FilteringLogObserver(self.filteredPublisher,
+ (self.levels,))
</ins><span class="cx"> self.rootPublisher = LogPublisher(self.filters)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -862,6 +864,7 @@
</span><span class="cx"> def __init__(self, submapping):
</span><span class="cx"> self._submapping = submapping
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __getitem__(self, key):
</span><span class="cx"> callit = key.endswith(u"()")
</span><span class="cx"> realKey = key[:-2] if callit else key
</span><span class="lines">@@ -871,6 +874,7 @@
</span><span class="cx"> return value
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def formatWithCall(formatString, mapping):
</span><span class="cx"> """
</span><span class="cx"> Format a string like L{unicode.format}, but:
</span><span class="lines">@@ -930,16 +934,20 @@
</span><span class="cx"> continue
</span><span class="cx">
</span><span class="cx"> for name, obj in module.__dict__.iteritems():
</span><del>- legacyLogger = LegacyLogger(logger=Logger(namespace=module.__name__))
</del><ins>+ newLogger = Logger(namespace=module.__name__)
+ legacyLogger = LegacyLogger(logger=newLogger)
</ins><span class="cx">
</span><span class="cx"> if obj is twisted.python.log:
</span><del>- log.info("Replacing Twisted log module object {0} in {1}".format(name, module.__name__))
</del><ins>+ log.info("Replacing Twisted log module object {0} in {1}"
+ .format(name, module.__name__))
</ins><span class="cx"> setattr(module, name, legacyLogger)
</span><span class="cx"> elif obj is twisted.python.log.msg:
</span><del>- log.info("Replacing Twisted log.msg object {0} in {1}".format(name, module.__name__))
</del><ins>+ log.info("Replacing Twisted log.msg object {0} in {1}"
+ .format(name, module.__name__))
</ins><span class="cx"> setattr(module, name, legacyLogger.msg)
</span><span class="cx"> elif obj is twisted.python.log.err:
</span><del>- log.info("Replacing Twisted log.err object {0} in {1}".format(name, module.__name__))
</del><ins>+ log.info("Replacing Twisted log.err object {0} in {1}"
+ .format(name, module.__name__))
</ins><span class="cx"> setattr(module, name, legacyLogger.err)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextpythontesttest_logpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/test/test_log.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/test/test_log.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/python/test/test_log.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -23,11 +23,11 @@
</span><span class="cx"> from twext.python.log import (
</span><span class="cx"> LogLevel, InvalidLogLevelError,
</span><span class="cx"> pythonLogLevelMapping,
</span><del>- formatEvent, formatWithCall,
</del><ins>+ formatEvent, formatUnformattableEvent, formatWithCall,
</ins><span class="cx"> Logger, LegacyLogger,
</span><del>- ILogObserver, LogPublisher,
</del><ins>+ ILogObserver, LogPublisher, DefaultLogPublisher,
</ins><span class="cx"> FilteringLogObserver, PredicateResult,
</span><del>- LogLevelFilterPredicate,
</del><ins>+ LogLevelFilterPredicate, OBSERVER_REMOVED
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -59,7 +59,7 @@
</span><span class="cx"> twistedLogging.removeObserver(observer)
</span><span class="cx">
</span><span class="cx"> self.emitted = {
</span><del>- "level" : level,
</del><ins>+ "level": level,
</ins><span class="cx"> "format": format,
</span><span class="cx"> "kwargs": kwargs,
</span><span class="cx"> }
</span><span class="lines">@@ -67,8 +67,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class TestLegacyLogger(LegacyLogger):
</span><del>- def __init__(self):
- LegacyLogger.__init__(self, logger=TestLogger())
</del><ins>+ def __init__(self, logger=TestLogger()):
+ LegacyLogger.__init__(self, logger=logger)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -131,7 +131,8 @@
</span><span class="cx"> """
</span><span class="cx"> self.failUnless(logLevelForNamespace(None), defaultLogLevel)
</span><span class="cx"> self.failUnless(logLevelForNamespace(""), defaultLogLevel)
</span><del>- self.failUnless(logLevelForNamespace("rocker.cool.namespace"), defaultLogLevel)
</del><ins>+ self.failUnless(logLevelForNamespace("rocker.cool.namespace"),
+ defaultLogLevel)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_setLogLevel(self):
</span><span class="lines">@@ -142,22 +143,30 @@
</span><span class="cx"> setLogLevelForNamespace("twext.web2", LogLevel.debug)
</span><span class="cx"> setLogLevelForNamespace("twext.web2.dav", LogLevel.warn)
</span><span class="cx">
</span><del>- self.assertEquals(logLevelForNamespace(None ), LogLevel.error)
- self.assertEquals(logLevelForNamespace("twisted" ), LogLevel.error)
- self.assertEquals(logLevelForNamespace("twext.web2" ), LogLevel.debug)
- self.assertEquals(logLevelForNamespace("twext.web2.dav" ), LogLevel.warn)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test" ), LogLevel.warn)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"), LogLevel.warn)
</del><ins>+ self.assertEquals(logLevelForNamespace(None),
+ LogLevel.error)
+ self.assertEquals(logLevelForNamespace("twisted"),
+ LogLevel.error)
+ self.assertEquals(logLevelForNamespace("twext.web2"),
+ LogLevel.debug)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav"),
+ LogLevel.warn)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav.test"),
+ LogLevel.warn)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"),
+ LogLevel.warn)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_setInvalidLogLevel(self):
</span><span class="cx"> """
</span><span class="cx"> Can't pass invalid log levels to setLogLevelForNamespace().
</span><span class="cx"> """
</span><del>- self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace, "twext.web2", object())
</del><ins>+ self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace,
+ "twext.web2", object())
</ins><span class="cx">
</span><span class="cx"> # Level must be a constant, not the name of a constant
</span><del>- self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace, "twext.web2", "debug")
</del><ins>+ self.assertRaises(InvalidLogLevelError, setLogLevelForNamespace,
+ "twext.web2", "debug")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_clearLogLevels(self):
</span><span class="lines">@@ -169,11 +178,14 @@
</span><span class="cx">
</span><span class="cx"> clearLogLevels()
</span><span class="cx">
</span><del>- self.assertEquals(logLevelForNamespace("twisted" ), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2" ), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav" ), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test" ), defaultLogLevel)
- self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"), defaultLogLevel)
</del><ins>+ self.assertEquals(logLevelForNamespace("twisted"), defaultLogLevel)
+ self.assertEquals(logLevelForNamespace("twext.web2"), defaultLogLevel)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav"),
+ defaultLogLevel)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav.test"),
+ defaultLogLevel)
+ self.assertEquals(logLevelForNamespace("twext.web2.dav.test1.test2"),
+ defaultLogLevel)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_namespace_default(self):
</span><span class="lines">@@ -191,14 +203,17 @@
</span><span class="cx"> mean that the format key ought to be I{called} rather than stringified.
</span><span class="cx"> """
</span><span class="cx"> self.assertEquals(
</span><del>- formatWithCall(u"Hello, {world}. {callme()}.",
- dict(world="earth",
- callme=lambda: "maybe")),
</del><ins>+ formatWithCall(
+ u"Hello, {world}. {callme()}.",
+ dict(world="earth", callme=lambda: "maybe")
+ ),
</ins><span class="cx"> "Hello, earth. maybe."
</span><span class="cx"> )
</span><span class="cx"> self.assertEquals(
</span><del>- formatWithCall(u"Hello, {repr()!r}.",
- dict(repr=lambda: 'repr')),
</del><ins>+ formatWithCall(
+ u"Hello, {repr()!r}.",
+ dict(repr=lambda: "repr")
+ ),
</ins><span class="cx"> "Hello, 'repr'."
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -262,7 +277,7 @@
</span><span class="cx"> self.assertIn(repr(event), result)
</span><span class="cx">
</span><span class="cx">
</span><del>- def test_formatEventYouSoNasty(self):
</del><ins>+ def test_formatUnformattableEvent(self):
</ins><span class="cx"> """
</span><span class="cx"> Formatting an event that's just plain out to get us.
</span><span class="cx"> """
</span><span class="lines">@@ -273,24 +288,52 @@
</span><span class="cx"> self.assertIn(repr(event), result)
</span><span class="cx">
</span><span class="cx">
</span><del>-# def test_formatEventYouSoNastyOMGMakeItStop(self):
-# """
-# Formatting an event that's just plain out to get us and is
-# really determined.
-# """
-# badRepr =
</del><ins>+ def test_formatUnformattableEventWithUnformattableKey(self):
+ """
+ Formatting an unformattable event that has an unformattable key.
+ """
+ event = {
+ "log_format": "{evil()}",
+ "evil": lambda: 1/0,
+ Unformattable(): "gurk",
+ }
+ result = formatEvent(event)
+ self.assertIn("MESSAGE LOST: unformattable object logged:", result)
+ self.assertIn("Recoverable data:", result)
+ self.assertIn("Exception during formatting:", result)
</ins><span class="cx">
</span><del>-# event = dict(
-# log_format="{evil()}",
-# evil=lambda: 1/0,
-# )
-# result = formatEvent(event)
</del><span class="cx">
</span><del>-# self.assertIn("Unable to format event", result)
-# self.assertIn(repr(event), result)
</del><ins>+ def test_formatUnformattableEventWithUnformattableValue(self):
+ """
+ Formatting an unformattable event that has an unformattable value.
+ """
+ event = dict(
+ log_format="{evil()}",
+ evil=lambda: 1/0,
+ gurk=Unformattable(),
+ )
+ result = formatEvent(event)
+ self.assertIn("MESSAGE LOST: unformattable object logged:", result)
+ self.assertIn("Recoverable data:", result)
+ self.assertIn("Exception during formatting:", result)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def test_formatUnformattableEventWithUnformattableErrorOMGWillItStop(self):
+ """
+ Formatting an unformattable event that has an unformattable value.
+ """
+ event = dict(
+ log_format="{evil()}",
+ evil=lambda: 1/0,
+ recoverable="okay",
+ )
+ # Call formatUnformattableEvent() directly with a bogus exception.
+ result = formatUnformattableEvent(event, Unformattable())
+ self.assertIn("MESSAGE LOST: unformattable object logged:", result)
+ self.assertIn(repr("recoverable") + " = " + repr("okay"), result)
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class LoggerTests(SetUpTearDown, unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for L{Logger}.
</span><span class="lines">@@ -322,8 +365,8 @@
</span><span class="cx">
</span><span class="cx"> def test_sourceAvailableForFormatting(self):
</span><span class="cx"> """
</span><del>- On instances that have a L{Logger} class attribute, the C{log_source} key
- is available to format strings.
</del><ins>+ On instances that have a L{Logger} class attribute, the C{log_source}
+ key is available to format strings.
</ins><span class="cx"> """
</span><span class="cx"> obj = LogComposedObject("hello")
</span><span class="cx"> log = obj.log
</span><span class="lines">@@ -359,16 +402,19 @@
</span><span class="cx"> self.assertEquals(log.emitted["kwargs"]["junk"], message)
</span><span class="cx">
</span><span class="cx"> if level >= logLevelForNamespace(log.namespace):
</span><ins>+ self.assertTrue(hasattr(log, "event"), "No event observed.")
</ins><span class="cx"> self.assertEquals(log.event["log_format"], format)
</span><span class="cx"> self.assertEquals(log.event["log_level"], level)
</span><span class="cx"> self.assertEquals(log.event["log_namespace"], __name__)
</span><span class="cx"> self.assertEquals(log.event["log_source"], None)
</span><span class="cx">
</span><del>- self.assertEquals(log.event["logLevel"], pythonLogLevelMapping[level])
</del><ins>+ self.assertEquals(log.event["logLevel"],
+ pythonLogLevelMapping[level])
</ins><span class="cx">
</span><span class="cx"> self.assertEquals(log.event["junk"], message)
</span><span class="cx">
</span><del>- # FIXME: this checks the end of message because we do formatting in emit()
</del><ins>+ # FIXME: this checks the end of message because we do
+ # formatting in emit()
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> formatEvent(log.event),
</span><span class="cx"> message
</span><span class="lines">@@ -407,10 +453,10 @@
</span><span class="cx">
</span><span class="cx"> log.warn(
</span><span class="cx"> "*",
</span><del>- log_format = "#",
- log_level = LogLevel.error,
- log_namespace = "*namespace*",
- log_source = "*source*",
</del><ins>+ log_format="#",
+ log_level=LogLevel.error,
+ log_namespace="*namespace*",
+ log_source="*source*",
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # FIXME: Should conflicts log errors?
</span><span class="lines">@@ -487,24 +533,232 @@
</span><span class="cx"> self.assertEquals(set((o1, o3)), set(publisher.observers))
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_removeObserverNotRegistered(self):
+ """
+ L{LogPublisher.removeObserver} removes an observer that is not
+ registered.
+ """
+ o1 = lambda e: None
+ o2 = lambda e: None
+ o3 = lambda e: None
+
+ publisher = LogPublisher(o1, o2)
+ publisher.removeObserver(o3)
+ self.assertEquals(set((o1, o2)), set(publisher.observers))
+
+
</ins><span class="cx"> def test_fanOut(self):
</span><span class="cx"> """
</span><span class="cx"> L{LogPublisher} calls its observers.
</span><span class="cx"> """
</span><del>- e1 = []
- e2 = []
- e3 = []
</del><ins>+ event = dict(foo=1, bar=2)
</ins><span class="cx">
</span><del>- o1 = lambda e: e1.append(e)
- o2 = lambda e: e2.append(e)
- o3 = lambda e: e3.append(e)
</del><ins>+ events1 = []
+ events2 = []
+ events3 = []
</ins><span class="cx">
</span><ins>+ o1 = lambda e: events1.append(e)
+ o2 = lambda e: events2.append(e)
+ o3 = lambda e: events3.append(e)
+
</ins><span class="cx"> publisher = LogPublisher(o1, o2, o3)
</span><ins>+ publisher(event)
+ self.assertIn(event, events1)
+ self.assertIn(event, events2)
+ self.assertIn(event, events3)
+
+
+ def test_observerRaises(self):
+ nonTestEvents = []
+ Logger.publisher.addObserver(lambda e: nonTestEvents.append(e))
+
+ event = dict(foo=1, bar=2)
+ exception = RuntimeError("ARGH! EVIL DEATH!")
+
+ events = []
+
+ def observer(event):
+ events.append(event)
+ raise exception
+
+ publisher = LogPublisher(observer)
+ publisher(event)
+
+ # Verify that the observer saw my event
+ self.assertIn(event, events)
+
+ # Verify that the observer raised my exception
+ errors = self.flushLoggedErrors(exception.__class__)
+ self.assertEquals(len(errors), 1)
+ self.assertIdentical(errors[0].value, exception)
+
+ # Verify that the exception was logged
+ for event in nonTestEvents:
+ if (
+ event.get("log_format", None) == OBSERVER_REMOVED and
+ getattr(event.get("failure", None), "value") is exception
+ ):
+ break
+ else:
+ self.fail("Observer raised an exception "
+ "and the exception was not logged.")
+
+
+ def test_observerRaisesAndLoggerHatesMe(self):
+ nonTestEvents = []
+ Logger.publisher.addObserver(lambda e: nonTestEvents.append(e))
+
+ event = dict(foo=1, bar=2)
+ exception = RuntimeError("ARGH! EVIL DEATH!")
+
+ def observer(event):
+ raise RuntimeError("Sad panda")
+
+ class GurkLogger(Logger):
+ def failure(self, *args, **kwargs):
+ raise exception
+
+ publisher = LogPublisher(observer)
+ publisher.log = GurkLogger()
+ publisher(event)
+
+ # Here, the lack of an exception thus far is a success, of sorts
+
+
+
+class DefaultLogPublisherTests(SetUpTearDown, unittest.TestCase):
+ def test_addObserver(self):
+ o1 = lambda e: None
+ o2 = lambda e: None
+ o3 = lambda e: None
+
+ publisher = DefaultLogPublisher()
+ publisher.addObserver(o1)
+ publisher.addObserver(o2, filtered=True)
+ publisher.addObserver(o3, filtered=False)
+
+ self.assertEquals(
+ set((o1, o2, publisher.legacyLogObserver)),
+ set(publisher.filteredPublisher.observers),
+ "Filtered observers do not match expected set"
+ )
+ self.assertEquals(
+ set((o3, publisher.filters)),
+ set(publisher.rootPublisher.observers),
+ "Root observers do not match expected set"
+ )
+
+
+ def test_addObserverAgain(self):
+ o1 = lambda e: None
+ o2 = lambda e: None
+ o3 = lambda e: None
+
+ publisher = DefaultLogPublisher()
+ publisher.addObserver(o1)
+ publisher.addObserver(o2, filtered=True)
+ publisher.addObserver(o3, filtered=False)
+
+ # Swap filtered-ness of o2 and o3
+ publisher.addObserver(o1)
+ publisher.addObserver(o2, filtered=False)
+ publisher.addObserver(o3, filtered=True)
+
+ self.assertEquals(
+ set((o1, o3, publisher.legacyLogObserver)),
+ set(publisher.filteredPublisher.observers),
+ "Filtered observers do not match expected set"
+ )
+ self.assertEquals(
+ set((o2, publisher.filters)),
+ set(publisher.rootPublisher.observers),
+ "Root observers do not match expected set"
+ )
+
+
+ def test_removeObserver(self):
+ o1 = lambda e: None
+ o2 = lambda e: None
+ o3 = lambda e: None
+
+ publisher = DefaultLogPublisher()
+ publisher.addObserver(o1)
+ publisher.addObserver(o2, filtered=True)
+ publisher.addObserver(o3, filtered=False)
</ins><span class="cx"> publisher.removeObserver(o2)
</span><del>- self.assertEquals(set((o1, o3)), set(publisher.observers))
</del><ins>+ publisher.removeObserver(o3)
</ins><span class="cx">
</span><ins>+ self.assertEquals(
+ set((o1, publisher.legacyLogObserver)),
+ set(publisher.filteredPublisher.observers),
+ "Filtered observers do not match expected set"
+ )
+ self.assertEquals(
+ set((publisher.filters,)),
+ set(publisher.rootPublisher.observers),
+ "Root observers do not match expected set"
+ )
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def test_filteredObserver(self):
+ namespace = __name__
+
+ event_debug = dict(log_namespace=namespace,
+ log_level=LogLevel.debug, log_format="")
+ event_error = dict(log_namespace=namespace,
+ log_level=LogLevel.error, log_format="")
+ events = []
+
+ observer = lambda e: events.append(e)
+
+ publisher = DefaultLogPublisher()
+
+ publisher.addObserver(observer, filtered=True)
+ publisher(event_debug)
+ publisher(event_error)
+ self.assertNotIn(event_debug, events)
+ self.assertIn(event_error, events)
+
+
+ def test_filteredObserverNoFilteringKeys(self):
+ event_debug = dict(log_level=LogLevel.debug)
+ event_error = dict(log_level=LogLevel.error)
+ event_none = dict()
+ events = []
+
+ observer = lambda e: events.append(e)
+
+ publisher = DefaultLogPublisher()
+ publisher.addObserver(observer, filtered=True)
+ publisher(event_debug)
+ publisher(event_error)
+ publisher(event_none)
+ self.assertNotIn(event_debug, events)
+ self.assertNotIn(event_error, events)
+ self.assertNotIn(event_none, events)
+
+
+ def test_unfilteredObserver(self):
+ namespace = __name__
+
+ event_debug = dict(log_namespace=namespace, log_level=LogLevel.debug,
+ log_format="")
+ event_error = dict(log_namespace=namespace, log_level=LogLevel.error,
+ log_format="")
+ events = []
+
+ observer = lambda e: events.append(e)
+
+ publisher = DefaultLogPublisher()
+
+ publisher.addObserver(observer, filtered=False)
+ publisher(event_debug)
+ publisher(event_error)
+ self.assertIn(event_debug, events)
+ self.assertIn(event_error, events)
+
+
+
</ins><span class="cx"> class FilteringLogObserverTests(SetUpTearDown, unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for L{FilteringLogObserver}.
</span><span class="lines">@@ -552,11 +806,16 @@
</span><span class="cx"> def no(event):
</span><span class="cx"> return PredicateResult.no
</span><span class="cx">
</span><ins>+ @staticmethod
+ def bogus(event):
+ return None
+
</ins><span class="cx"> predicates = (getattr(Filters, f) for f in filters)
</span><span class="cx"> eventsSeen = []
</span><span class="cx"> trackingObserver = lambda e: eventsSeen.append(e)
</span><span class="cx"> filteringObserver = FilteringLogObserver(trackingObserver, predicates)
</span><del>- for e in events: filteringObserver(e)
</del><ins>+ for e in events:
+ filteringObserver(e)
</ins><span class="cx">
</span><span class="cx"> return [e["count"] for e in eventsSeen]
</span><span class="cx">
</span><span class="lines">@@ -564,25 +823,35 @@
</span><span class="cx"> def test_shouldLogEvent_noFilters(self):
</span><span class="cx"> self.assertEquals(self.filterWith(), [0, 1, 2, 3])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_shouldLogEvent_noFilter(self):
</span><span class="cx"> self.assertEquals(self.filterWith("notTwo"), [0, 1, 3])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_shouldLogEvent_yesFilter(self):
</span><span class="cx"> self.assertEquals(self.filterWith("twoPlus"), [0, 1, 2, 3])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_shouldLogEvent_yesNoFilter(self):
</span><span class="cx"> self.assertEquals(self.filterWith("twoPlus", "no"), [2, 3])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_shouldLogEvent_yesYesNoFilter(self):
</span><del>- self.assertEquals(self.filterWith("twoPlus", "twoMinus", "no"), [0, 1, 2, 3])
</del><ins>+ self.assertEquals(self.filterWith("twoPlus", "twoMinus", "no"),
+ [0, 1, 2, 3])
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def test_shouldLogEvent_badPredicateResult(self):
+ self.assertRaises(TypeError, self.filterWith, "bogus")
+
+
</ins><span class="cx"> def test_call(self):
</span><span class="cx"> e = dict(obj=object())
</span><span class="cx">
</span><span class="cx"> def callWithPredicateResult(result):
</span><span class="cx"> seen = []
</span><del>- observer = FilteringLogObserver(lambda e: seen.append(e), (lambda e: result,))
</del><ins>+ observer = FilteringLogObserver(lambda e: seen.append(e),
+ (lambda e: result,))
</ins><span class="cx"> observer(e)
</span><span class="cx"> return seen
</span><span class="cx">
</span><span class="lines">@@ -597,6 +866,14 @@
</span><span class="cx"> Tests for L{LegacyLogger}.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ def test_namespace_default(self):
+ """
+ Default namespace is module name.
+ """
+ log = TestLegacyLogger(logger=None)
+ self.assertEquals(log.newStyleLogger.namespace, __name__)
+
+
</ins><span class="cx"> def test_passThroughAttributes(self):
</span><span class="cx"> """
</span><span class="cx"> C{__getattribute__} on L{LegacyLogger} is passing through to Twisted's
</span><span class="lines">@@ -619,19 +896,22 @@
</span><span class="cx"> log = TestLegacyLogger()
</span><span class="cx">
</span><span class="cx"> message = "Hi, there."
</span><del>- kwargs = { "foo": "bar", "obj": object() }
</del><ins>+ kwargs = {"foo": "bar", "obj": object()}
</ins><span class="cx">
</span><span class="cx"> log.msg(message, **kwargs)
</span><span class="cx">
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["level"], LogLevel.info)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["level"],
+ LogLevel.info)
</ins><span class="cx"> self.assertEquals(log.newStyleLogger.emitted["format"], message)
</span><span class="cx">
</span><span class="cx"> for key, value in kwargs.items():
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key], value)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
+ value)
</ins><span class="cx">
</span><span class="cx"> log.msg(foo="")
</span><span class="cx">
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["level"], LogLevel.info)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["level"],
+ LogLevel.info)
</ins><span class="cx"> self.assertIdentical(log.newStyleLogger.emitted["format"], None)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -642,7 +922,7 @@
</span><span class="cx"> log = TestLegacyLogger()
</span><span class="cx">
</span><span class="cx"> exception = RuntimeError("Oh me, oh my.")
</span><del>- kwargs = { "foo": "bar", "obj": object() }
</del><ins>+ kwargs = {"foo": "bar", "obj": object()}
</ins><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> raise exception
</span><span class="lines">@@ -659,7 +939,7 @@
</span><span class="cx"> log = TestLegacyLogger()
</span><span class="cx">
</span><span class="cx"> exception = RuntimeError("Oh me, oh my.")
</span><del>- kwargs = { "foo": "bar", "obj": object() }
</del><ins>+ kwargs = {"foo": "bar", "obj": object()}
</ins><span class="cx"> why = "Because I said so."
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -677,7 +957,7 @@
</span><span class="cx"> log = TestLegacyLogger()
</span><span class="cx">
</span><span class="cx"> exception = RuntimeError("Oh me, oh my.")
</span><del>- kwargs = { "foo": "bar", "obj": object() }
</del><ins>+ kwargs = {"foo": "bar", "obj": object()}
</ins><span class="cx"> why = "Because I said so."
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -695,7 +975,7 @@
</span><span class="cx"> log = TestLegacyLogger()
</span><span class="cx">
</span><span class="cx"> exception = RuntimeError("Oh me, oh my.")
</span><del>- kwargs = { "foo": "bar", "obj": object() }
</del><ins>+ kwargs = {"foo": "bar", "obj": object()}
</ins><span class="cx"> why = "Because I said so."
</span><span class="cx"> bogus = object()
</span><span class="cx">
</span><span class="lines">@@ -707,12 +987,14 @@
</span><span class="cx"> errors = self.flushLoggedErrors(exception.__class__)
</span><span class="cx"> self.assertEquals(len(errors), 0)
</span><span class="cx">
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["level"], LogLevel.error)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["level"],
+ LogLevel.error)
</ins><span class="cx"> self.assertEquals(log.newStyleLogger.emitted["format"], repr(bogus))
</span><span class="cx"> self.assertIdentical(log.newStyleLogger.emitted["kwargs"]["why"], why)
</span><span class="cx">
</span><span class="cx"> for key, value in kwargs.items():
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key], value)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
+ value)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def legacy_err(self, log, kwargs, why, exception):
</span><span class="lines">@@ -724,11 +1006,24 @@
</span><span class="cx"> errors = self.flushLoggedErrors(exception.__class__)
</span><span class="cx"> self.assertEquals(len(errors), 1)
</span><span class="cx">
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["level"], LogLevel.error)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["level"],
+ LogLevel.error)
</ins><span class="cx"> self.assertEquals(log.newStyleLogger.emitted["format"], None)
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["kwargs"]["failure"].__class__, Failure)
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"]["failure"].value, exception)
- self.assertIdentical(log.newStyleLogger.emitted["kwargs"]["why"], why)
</del><ins>+ emittedKwargs = log.newStyleLogger.emitted["kwargs"]
+ self.assertIdentical(emittedKwargs["failure"].__class__, Failure)
+ self.assertIdentical(emittedKwargs["failure"].value, exception)
+ self.assertIdentical(emittedKwargs["why"], why)
</ins><span class="cx">
</span><span class="cx"> for key, value in kwargs.items():
</span><del>- self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key], value)
</del><ins>+ self.assertIdentical(log.newStyleLogger.emitted["kwargs"][key],
+ value)
+
+
+
+class Unformattable(object):
+ """
+ An object that raises an exception from C{__repr__}.
+ """
+
+ def __repr__(self):
+ return str(1/0)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2channelhttppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/channel/http.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/channel/http.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/channel/http.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -726,6 +726,10 @@
</span><span class="cx"> betweenRequestsTimeOut = 15
</span><span class="cx"> # Timeout between lines or bytes while reading a request
</span><span class="cx"> inputTimeOut = 60 * 4
</span><ins>+ # Timeout between end of request read and end of response write
+ idleTimeOut = 60 * 5
+ # Timeout when closing non-persistent connection
+ closeTimeOut = 20
</ins><span class="cx">
</span><span class="cx"> # maximum length of headers (10KiB)
</span><span class="cx"> maxHeaderLength = 10240
</span><span class="lines">@@ -744,7 +748,7 @@
</span><span class="cx"> _readLost = False
</span><span class="cx"> _writeLost = False
</span><span class="cx">
</span><del>- _lingerTimer = None
</del><ins>+ _abortTimer = None
</ins><span class="cx"> chanRequest = None
</span><span class="cx">
</span><span class="cx"> def _callLater(self, secs, fun):
</span><span class="lines">@@ -823,10 +827,10 @@
</span><span class="cx"> self.chanRequest = None
</span><span class="cx"> self.setLineMode()
</span><span class="cx">
</span><del>- # Disable the idle timeout, in case this request takes a long
</del><ins>+ # Set an idle timeout, in case this request takes a long
</ins><span class="cx"> # time to finish generating output.
</span><span class="cx"> if len(self.requests) > 0:
</span><del>- self.setTimeout(None)
</del><ins>+ self.setTimeout(self.idleTimeOut)
</ins><span class="cx">
</span><span class="cx"> def _startNextRequest(self):
</span><span class="cx"> # notify next request, if present, it can start writing
</span><span class="lines">@@ -881,57 +885,29 @@
</span><span class="cx"> # incoming requests.
</span><span class="cx"> self._callLater(0, self._startNextRequest)
</span><span class="cx"> else:
</span><del>- self.lingeringClose()
</del><ins>+ # Set an abort timer in case an orderly close hangs
+ self.setTimeout(None)
+ self._abortTimer = reactor.callLater(self.closeTimeOut, self._abortTimeout)
+ #reactor.callLater(0.1, self.transport.loseConnection)
+ self.transport.loseConnection()
</ins><span class="cx">
</span><span class="cx"> def timeoutConnection(self):
</span><span class="cx"> #log.info("Timing out client: %s" % str(self.transport.getPeer()))
</span><ins>+ # Set an abort timer in case an orderly close hangs
+ self._abortTimer = reactor.callLater(self.closeTimeOut, self._abortTimeout)
</ins><span class="cx"> policies.TimeoutMixin.timeoutConnection(self)
</span><span class="cx">
</span><del>- def lingeringClose(self):
- """
- This is a bit complicated. This process is necessary to ensure proper
- workingness when HTTP pipelining is in use.
</del><ins>+ def _abortTimeout(self):
+ log.error("Connection aborted - took too long to close: {c}", c=str(self.transport.getPeer()))
+ self._abortTimer = None
+ self.transport.abortConnection()
</ins><span class="cx">
</span><del>- Here is what it wants to do:
-
- 1. Finish writing any buffered data, then close our write side.
- While doing so, read and discard any incoming data.
-
- 2. When that happens (writeConnectionLost called), wait up to 20
- seconds for the remote end to close their write side (our read
- side).
-
- 3.
- - If they do (readConnectionLost called), close the socket,
- and cancel the timeout.
-
- - If that doesn't happen, the timer fires, and makes the
- socket close anyways.
- """
-
- # Close write half
- self.transport.loseWriteConnection()
-
- # Throw out any incoming data
- self.dataReceived = self.lineReceived = lambda *args: None
- self.transport.resumeProducing()
-
- def writeConnectionLost(self):
- # Okay, all data has been written
- # In 20 seconds, actually close the socket
- self._lingerTimer = reactor.callLater(20, self._lingerClose)
- self._writeLost = True
-
- def _lingerClose(self):
- self._lingerTimer = None
- self.transport.loseConnection()
-
</del><span class="cx"> def readConnectionLost(self):
</span><span class="cx"> """Read connection lost"""
</span><span class="cx"> # If in the lingering-close state, lose the socket.
</span><del>- if self._lingerTimer:
- self._lingerTimer.cancel()
- self._lingerTimer = None
</del><ins>+ if self._abortTimer:
+ self._abortTimer.cancel()
+ self._abortTimer = None
</ins><span class="cx"> self.transport.loseConnection()
</span><span class="cx"> return
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2davtesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/test/test_util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/test/test_util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/test/test_util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -42,6 +42,7 @@
</span><span class="cx"> self.assertEquals(util.normalizeURL("///../"), "/")
</span><span class="cx"> self.assertEquals(util.normalizeURL("/.."), "/")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_joinURL(self):
</span><span class="cx"> """
</span><span class="cx"> joinURL()
</span><span class="lines">@@ -67,6 +68,7 @@
</span><span class="cx"> self.assertEquals(util.joinURL("/foo", "/../"), "/")
</span><span class="cx"> self.assertEquals(util.joinURL("/foo", "/./"), "/foo/")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_parentForURL(self):
</span><span class="cx"> """
</span><span class="cx"> parentForURL()
</span><span class="lines">@@ -83,6 +85,8 @@
</span><span class="cx"> self.assertEquals(util.parentForURL("http://server/foo/bar/."), "http://server/foo/")
</span><span class="cx"> self.assertEquals(util.parentForURL("http://server/foo/bar"), "http://server/foo/")
</span><span class="cx"> self.assertEquals(util.parentForURL("http://server/foo/bar/"), "http://server/foo/")
</span><ins>+ self.assertEquals(util.parentForURL("http://server/foo/bar?x=1&y=2"), "http://server/foo/")
+ self.assertEquals(util.parentForURL("http://server/foo/bar/?x=1&y=2"), "http://server/foo/")
</ins><span class="cx"> self.assertEquals(util.parentForURL("/"), None)
</span><span class="cx"> self.assertEquals(util.parentForURL("/foo/.."), None)
</span><span class="cx"> self.assertEquals(util.parentForURL("/foo/../"), None)
</span><span class="lines">@@ -94,3 +98,5 @@
</span><span class="cx"> self.assertEquals(util.parentForURL("/foo/bar/."), "/foo/")
</span><span class="cx"> self.assertEquals(util.parentForURL("/foo/bar"), "/foo/")
</span><span class="cx"> self.assertEquals(util.parentForURL("/foo/bar/"), "/foo/")
</span><ins>+ self.assertEquals(util.parentForURL("/foo/bar?x=1&y=2"), "/foo/")
+ self.assertEquals(util.parentForURL("/foo/bar/?x=1&y=2"), "/foo/")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2davutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/dav/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -61,7 +61,8 @@
</span><span class="cx"> def allDataFromStream(stream, filter=None):
</span><span class="cx"> data = []
</span><span class="cx"> def gotAllData(_):
</span><del>- if not data: return None
</del><ins>+ if not data:
+ return None
</ins><span class="cx"> result = "".join([str(x) for x in data])
</span><span class="cx"> if filter is None:
</span><span class="cx"> return result
</span><span class="lines">@@ -69,6 +70,8 @@
</span><span class="cx"> return filter(result)
</span><span class="cx"> return readStream(stream, data.append).addCallback(gotAllData)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def davXMLFromStream(stream):
</span><span class="cx"> # FIXME:
</span><span class="cx"> # This reads the request body into a string and then parses it.
</span><span class="lines">@@ -77,6 +80,7 @@
</span><span class="cx"> if stream is None:
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def parse(xml):
</span><span class="cx"> try:
</span><span class="cx"> doc = WebDAVDocument.fromString(xml)
</span><span class="lines">@@ -87,11 +91,16 @@
</span><span class="cx"> raise
</span><span class="cx"> return allDataFromStream(stream, parse)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def noDataFromStream(stream):
</span><span class="cx"> def gotData(data):
</span><del>- if data: raise ValueError("Stream contains unexpected data.")
</del><ins>+ if data:
+ raise ValueError("Stream contains unexpected data.")
</ins><span class="cx"> return readStream(stream, gotData)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> ##
</span><span class="cx"> # URLs
</span><span class="cx"> ##
</span><span class="lines">@@ -111,9 +120,10 @@
</span><span class="cx"> if path[0] == "/":
</span><span class="cx"> count = 0
</span><span class="cx"> for char in path:
</span><del>- if char != "/": break
</del><ins>+ if char != "/":
+ break
</ins><span class="cx"> count += 1
</span><del>- path = path[count-1:]
</del><ins>+ path = path[count - 1:]
</ins><span class="cx">
</span><span class="cx"> return path
</span><span class="cx">
</span><span class="lines">@@ -123,6 +133,8 @@
</span><span class="cx">
</span><span class="cx"> return urlunsplit((scheme, host, urllib.quote(path), query, fragment))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def joinURL(*urls):
</span><span class="cx"> """
</span><span class="cx"> Appends URLs in series.
</span><span class="lines">@@ -142,16 +154,19 @@
</span><span class="cx"> else:
</span><span class="cx"> return url + trailing
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def parentForURL(url):
</span><span class="cx"> """
</span><span class="cx"> Extracts the URL of the containing collection resource for the resource
</span><del>- corresponding to a given URL.
</del><ins>+ corresponding to a given URL. This removes any query or fragment pieces.
+
</ins><span class="cx"> @param url: an absolute (server-relative is OK) URL.
</span><span class="cx"> @return: the normalized URL of the collection resource containing the
</span><span class="cx"> resource corresponding to C{url}. The returned URL will always contain
</span><span class="cx"> a trailing C{"/"}.
</span><span class="cx"> """
</span><del>- (scheme, host, path, query, fragment) = urlsplit(normalizeURL(url))
</del><ins>+ (scheme, host, path, _ignore_query, _ignore_fragment) = urlsplit(normalizeURL(url))
</ins><span class="cx">
</span><span class="cx"> index = path.rfind("/")
</span><span class="cx"> if index is 0:
</span><span class="lines">@@ -165,8 +180,10 @@
</span><span class="cx"> else:
</span><span class="cx"> path = path[:index] + "/"
</span><span class="cx">
</span><del>- return urlunsplit((scheme, host, path, query, fragment))
</del><ins>+ return urlunsplit((scheme, host, path, None, None))
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> ##
</span><span class="cx"> # Python magic
</span><span class="cx"> ##
</span><span class="lines">@@ -180,6 +197,8 @@
</span><span class="cx"> caller = inspect.getouterframes(inspect.currentframe())[1][3]
</span><span class="cx"> raise NotImplementedError("Method %s is unimplemented in subclass %s" % (caller, obj.__class__))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def bindMethods(module, clazz, prefixes=("preconditions_", "http_", "report_")):
</span><span class="cx"> """
</span><span class="cx"> Binds all functions in the given module (as defined by that module's
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2metafdpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/metafd.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/metafd.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/metafd.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -23,6 +23,8 @@
</span><span class="cx">
</span><span class="cx"> from functools import total_ordering
</span><span class="cx">
</span><ins>+from zope.interface import implementer
+
</ins><span class="cx"> from twext.internet.sendfdport import (
</span><span class="cx"> InheritedPort, InheritedSocketDispatcher, InheritingProtocolFactory)
</span><span class="cx"> from twext.internet.tcp import MaxAcceptTCPServer
</span><span class="lines">@@ -30,7 +32,9 @@
</span><span class="cx"> from twext.web2.channel.http import HTTPFactory
</span><span class="cx"> from twisted.application.service import MultiService, Service
</span><span class="cx"> from twisted.internet import reactor
</span><ins>+from twisted.python.util import FancyStrMixin
</ins><span class="cx"> from twisted.internet.tcp import Server
</span><ins>+from twext.internet.sendfdport import IStatusWatcher
</ins><span class="cx">
</span><span class="cx"> log = Logger()
</span><span class="cx">
</span><span class="lines">@@ -161,12 +165,16 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @total_ordering
</span><del>-class WorkerStatus(object):
</del><ins>+class WorkerStatus(FancyStrMixin, object):
</ins><span class="cx"> """
</span><span class="cx"> The status of a worker process.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, acknowledged=0, unacknowledged=0, started=0):
</del><ins>+ showAttributes = ("acknowledged unacknowledged started abandoned unclosed"
+ .split())
+
+ def __init__(self, acknowledged=0, unacknowledged=0, started=0,
+ abandoned=0, unclosed=0):
</ins><span class="cx"> """
</span><span class="cx"> Create a L{ConnectionStatus} with a number of sent connections and a
</span><span class="cx"> number of un-acknowledged connections.
</span><span class="lines">@@ -179,29 +187,45 @@
</span><span class="cx"> the subprocess which have never received a status response (a
</span><span class="cx"> "C{+}" status message).
</span><span class="cx">
</span><ins>+ @param abandoned: The number of connections which have been sent to
+ this worker, but were not acknowledged at the moment that the
+ worker restarted.
+
</ins><span class="cx"> @param started: The number of times this worker has been started.
</span><ins>+
+ @param unclosed: The number of sockets which have been sent to the
+ subprocess but not yet closed.
</ins><span class="cx"> """
</span><span class="cx"> self.acknowledged = acknowledged
</span><span class="cx"> self.unacknowledged = unacknowledged
</span><span class="cx"> self.started = started
</span><ins>+ self.abandoned = abandoned
+ self.unclosed = unclosed
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def effective(self):
+ """
+ The current effective load.
+ """
+ return self.acknowledged + self.unacknowledged
+
+
</ins><span class="cx"> def restarted(self):
</span><span class="cx"> """
</span><span class="cx"> The L{WorkerStatus} derived from the current status of a process and
</span><span class="cx"> the fact that it just restarted.
</span><span class="cx"> """
</span><del>- return self.__class__(0, self.unacknowledged, self.started + 1)
</del><ins>+ return self.__class__(0, 0, self.started + 1, self.unacknowledged)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _tuplify(self):
</span><del>- return (self.acknowledged, self.unacknowledged, self.started)
</del><ins>+ return tuple(getattr(self, attr) for attr in self.showAttributes)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __lt__(self, other):
</span><span class="cx"> if not isinstance(other, WorkerStatus):
</span><span class="cx"> return NotImplemented
</span><del>- return self._tuplify() < other._tuplify()
</del><ins>+ return self.effective() < other.effective()
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __eq__(self, other):
</span><span class="lines">@@ -213,20 +237,20 @@
</span><span class="cx"> def __add__(self, other):
</span><span class="cx"> if not isinstance(other, WorkerStatus):
</span><span class="cx"> return NotImplemented
</span><del>- return self.__class__(self.acknowledged + other.acknowledged,
- self.unacknowledged + other.unacknowledged,
- self.started + other.started)
</del><ins>+ a = self._tuplify()
+ b = other._tuplify()
+ c = [a1 + b1 for (a1, b1) in zip(a, b)]
+ return self.__class__(*c)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __sub__(self, other):
</span><span class="cx"> if not isinstance(other, WorkerStatus):
</span><span class="cx"> return NotImplemented
</span><del>- return self + self.__class__(-other.acknowledged,
- -other.unacknowledged,
- -other.started)
</del><ins>+ return self + self.__class__(*[-x for x in other._tuplify()])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+@implementer(IStatusWatcher)
</ins><span class="cx"> class ConnectionLimiter(MultiService, object):
</span><span class="cx"> """
</span><span class="cx"> Connection limiter for use with L{InheritedSocketDispatcher}.
</span><span class="lines">@@ -234,6 +258,8 @@
</span><span class="cx"> This depends on statuses being reported by L{ReportingHTTPFactory}
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ _outstandingRequests = 0
+
</ins><span class="cx"> def __init__(self, maxAccepts, maxRequests):
</span><span class="cx"> """
</span><span class="cx"> Create a L{ConnectionLimiter} with an associated dispatcher and
</span><span class="lines">@@ -300,9 +326,18 @@
</span><span class="cx"> else:
</span><span class="cx"> # '+' acknowledges that the subprocess has taken on the work.
</span><span class="cx"> return previousStatus + WorkerStatus(acknowledged=1,
</span><del>- unacknowledged=-1)
</del><ins>+ unacknowledged=-1,
+ unclosed=1)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def closeCountFromStatus(self, status):
+ """
+ Determine the number of sockets to close from the current status.
+ """
+ toClose = status.unclosed
+ return (toClose, status - WorkerStatus(unclosed=toClose))
+
+
</ins><span class="cx"> def newConnectionStatus(self, previousStatus):
</span><span class="cx"> """
</span><span class="cx"> Determine the effect of a new connection being sent on a subprocess
</span><span class="lines">@@ -320,20 +355,18 @@
</span><span class="cx"> C{self.dispatcher.statuses} attribute, which is what
</span><span class="cx"> C{self.outstandingRequests} uses to compute it.)
</span><span class="cx"> """
</span><del>- current = sum(status.acknowledged
</del><ins>+ current = sum(status.effective()
</ins><span class="cx"> for status in self.dispatcher.statuses)
</span><span class="cx"> self._outstandingRequests = current # preserve for or= field in log
</span><span class="cx"> maximum = self.maxRequests
</span><span class="cx"> overloaded = (current >= maximum)
</span><del>- if overloaded:
- for f in self.factories:
- f.myServer.myPort.stopReading()
- else:
- for f in self.factories:
- f.myServer.myPort.startReading()
</del><ins>+ for f in self.factories:
+ if overloaded:
+ f.loadAboveMaximum()
+ else:
+ f.loadNominal()
</ins><span class="cx">
</span><span class="cx">
</span><del>- _outstandingRequests = 0
</del><span class="cx"> @property # make read-only
</span><span class="cx"> def outstandingRequests(self):
</span><span class="cx"> return self._outstandingRequests
</span><span class="lines">@@ -367,6 +400,20 @@
</span><span class="cx"> self.maxRequests = limiter.maxRequests
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def loadAboveMaximum(self):
+ """
+ The current server load has exceeded the maximum allowable.
+ """
+ self.myServer.myPort.stopReading()
+
+
+ def loadNominal(self):
+ """
+ The current server load is nominal; proceed with reading requests.
+ """
+ self.myServer.myPort.startReading()
+
+
</ins><span class="cx"> @property
</span><span class="cx"> def outstandingRequests(self):
</span><span class="cx"> return self.limiter.outstandingRequests
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2testtest_httppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_http.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_http.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_http.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -14,7 +14,7 @@
</span><span class="cx"> from twisted.internet.defer import waitForDeferred, deferredGenerator
</span><span class="cx"> from twisted.protocols import loopback
</span><span class="cx"> from twisted.python import util, runtime
</span><del>-from twext.web2.channel.http import SSLRedirectRequest, HTTPFactory
</del><ins>+from twext.web2.channel.http import SSLRedirectRequest, HTTPFactory, HTTPChannel
</ins><span class="cx"> from twisted.internet.task import deferLater
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -319,6 +319,10 @@
</span><span class="cx"> self.loseConnection()
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def abortConnection(self):
+ self.aborted = True
+
+
</ins><span class="cx"> def getHost(self):
</span><span class="cx"> """
</span><span class="cx"> Synthesize a slightly more realistic 'host' thing.
</span><span class="lines">@@ -409,6 +413,13 @@
</span><span class="cx">
</span><span class="cx"> requestClass = TestRequest
</span><span class="cx">
</span><ins>+ def setUp(self):
+ super(HTTPTests, self).setUp()
+
+ # We always need this set to True - previous tests may have changed it
+ HTTPChannel.allowPersistentConnections = True
+
+
</ins><span class="cx"> def connect(self, logFile=None, **protocol_kwargs):
</span><span class="cx"> cxn = TestConnection()
</span><span class="cx">
</span><span class="lines">@@ -850,6 +861,42 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
</span><span class="cx">
</span><ins>+ def testTimeout_idleRequest(self):
+ cxn = self.connect(idleTimeOut=0.3)
+ cmds = [[]]
+ data = ""
+
+ cxn.client.write("GET / HTTP/1.1\r\n\r\n")
+ cmds[0] += [('init', 'GET', '/', (1, 1), 0, ()),
+ ('contentComplete',)]
+ self.compareResult(cxn, cmds, data)
+
+ return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
+
+ def testTimeout_abortRequest(self):
+ cxn = self.connect(allowPersistentConnections=False, closeTimeOut=0.3)
+ cxn.client.transport.loseConnection = lambda : None
+ cmds = [[]]
+ data = ""
+
+ cxn.client.write("GET / HTTP/1.1\r\n\r\n")
+ cmds[0] += [('init', 'GET', '/', (1, 1), 0, ()),
+ ('contentComplete',)]
+ self.compareResult(cxn, cmds, data)
+
+ response = TestResponse()
+ response.headers.setRawHeaders("Content-Length", ("0",))
+ cxn.requests[0].writeResponse(response)
+ response.finish()
+
+ data += "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
+
+ self.compareResult(cxn, cmds, data)
+ def _check(cxn):
+ self.assertDone(cxn)
+ self.assertTrue(cxn.serverToClient.aborted)
+ return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
+
</ins><span class="cx"> def testConnectionCloseRequested(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -883,6 +930,26 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+ def testConnectionKeepAliveOff(self):
+ cxn = self.connect(allowPersistentConnections=False)
+ cmds = [[]]
+ data = ""
+
+ cxn.client.write("GET / HTTP/1.1\r\n\r\n")
+ cmds[0] += [('init', 'GET', '/', (1, 1), 0, ()),
+ ('contentComplete',)]
+ self.compareResult(cxn, cmds, data)
+
+ response = TestResponse()
+ response.headers.setRawHeaders("Content-Length", ("0",))
+ cxn.requests[0].writeResponse(response)
+ response.finish()
+
+ data += "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
+
+ self.compareResult(cxn, cmds, data)
+ self.assertDone(cxn)
+
</ins><span class="cx"> def testExtraCRLFs(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextweb2testtest_metafdpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_metafd.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_metafd.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/web2/test/test_metafd.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> from twisted.application.service import Service
</span><span class="cx">
</span><span class="cx"> from twext.internet.test.test_sendfdport import ReaderAdder
</span><ins>+from twext.web2.metafd import WorkerStatus
</ins><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -60,6 +61,7 @@
</span><span class="cx"> return ("4.3.2.1", 4321)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class InheritedPortForTesting(sendfdport.InheritedPort):
</span><span class="cx"> """
</span><span class="cx"> L{sendfdport.InheritedPort} subclass that prevents certain I/O operations
</span><span class="lines">@@ -91,15 +93,19 @@
</span><span class="cx"> def startReading(self):
</span><span class="cx"> "Do nothing."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def stopReading(self):
</span><span class="cx"> "Do nothing."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def startWriting(self):
</span><span class="cx"> "Do nothing."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def stopWriting(self):
</span><span class="cx"> "Do nothing."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, *a, **kw):
</span><span class="cx"> super(ServerTransportForTesting, self).__init__(*a, **kw)
</span><span class="cx"> self.reactor = None
</span><span class="lines">@@ -163,6 +169,7 @@
</span><span class="cx"> builder = LimiterBuilder(self)
</span><span class="cx"> builder.fillUp()
</span><span class="cx"> self.assertEquals(builder.port.reading, False) # sanity check
</span><ins>+ self.assertEquals(builder.highestLoad(), builder.requestsPerSocket)
</ins><span class="cx"> builder.loadDown()
</span><span class="cx"> self.assertEquals(builder.port.reading, True)
</span><span class="cx">
</span><span class="lines">@@ -176,30 +183,87 @@
</span><span class="cx"> builder = LimiterBuilder(self)
</span><span class="cx"> builder.fillUp()
</span><span class="cx"> self.assertEquals(builder.port.reading, False)
</span><ins>+ self.assertEquals(builder.highestLoad(), builder.requestsPerSocket)
</ins><span class="cx"> builder.processRestart()
</span><span class="cx"> self.assertEquals(builder.port.reading, True)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_unevenLoadDistribution(self):
+ """
+ Subprocess sockets should be selected for subsequent socket sends by
+ ascending status. Status should sum sent and successfully subsumed
+ sockets.
+ """
+ builder = LimiterBuilder(self)
+ # Give one simulated worker a higher acknowledged load than the other.
+ builder.fillUp(True, 1)
+ # There should still be plenty of spare capacity.
+ self.assertEquals(builder.port.reading, True)
+ # Then slam it with a bunch of incoming requests.
+ builder.fillUp(False, builder.limiter.maxRequests - 1)
+ # Now capacity is full.
+ self.assertEquals(builder.port.reading, False)
+ # And everyone should have an even amount of work.
+ self.assertEquals(builder.highestLoad(), builder.requestsPerSocket)
</ins><span class="cx">
</span><ins>+
+ def test_processStopsReadingEvenWhenConnectionsAreNotAcknowledged(self):
+ """
+ L{ConnectionLimiter.statusesChanged} determines whether the current
+ number of outstanding requests is above the limit.
+ """
+ builder = LimiterBuilder(self)
+ builder.fillUp(acknowledged=False)
+ self.assertEquals(builder.highestLoad(), builder.requestsPerSocket)
+ self.assertEquals(builder.port.reading, False)
+ builder.processRestart()
+ self.assertEquals(builder.port.reading, True)
+
+
+ def test_workerStatusRepr(self):
+ """
+ L{WorkerStatus.__repr__} will show all the values associated with the
+ status of the worker.
+ """
+ self.assertEquals(repr(WorkerStatus(1, 2, 3, 4, 5)),
+ "<WorkerStatus acknowledged=1 unacknowledged=2 "
+ "started=3 abandoned=4 unclosed=5>")
+
+
+
</ins><span class="cx"> class LimiterBuilder(object):
</span><span class="cx"> """
</span><span class="cx"> A L{LimiterBuilder} can build a L{ConnectionLimiter} and associated objects
</span><span class="cx"> for a given unit test.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, test, maxReq=3):
- self.limiter = ConnectionLimiter(2, maxRequests=maxReq)
</del><ins>+ def __init__(self, test, requestsPerSocket=3, socketCount=2):
+ # Similar to MaxRequests in the configuration.
+ self.requestsPerSocket = requestsPerSocket
+ # Similar to ProcessCount in the configuration.
+ self.socketCount = socketCount
+ self.limiter = ConnectionLimiter(
+ 2, maxRequests=requestsPerSocket * socketCount
+ )
</ins><span class="cx"> self.dispatcher = self.limiter.dispatcher
</span><span class="cx"> self.dispatcher.reactor = ReaderAdder()
</span><span class="cx"> self.service = Service()
</span><span class="cx"> self.limiter.addPortService("TCP", 4321, "127.0.0.1", 5,
</span><span class="cx"> self.serverServiceMakerMaker(self.service))
</span><del>- self.dispatcher.addSocket()
</del><ins>+ for ignored in xrange(socketCount):
+ self.dispatcher.addSocket()
</ins><span class="cx"> # Has to be running in order to add stuff.
</span><span class="cx"> self.limiter.startService()
</span><span class="cx"> self.port = self.service.myPort
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def highestLoad(self):
+ return max(
+ skt.status.effective()
+ for skt in self.limiter.dispatcher._subprocessSockets
+ )
+
+
</ins><span class="cx"> def serverServiceMakerMaker(self, s):
</span><span class="cx"> """
</span><span class="cx"> Make a serverServiceMaker for use with
</span><span class="lines">@@ -214,21 +278,30 @@
</span><span class="cx"> def serverServiceMaker(port, factory, *a, **k):
</span><span class="cx"> s.factory = factory
</span><span class="cx"> s.myPort = NotAPort()
</span><del>- s.myPort.startReading() # TODO: technically, should wait for startService
</del><ins>+ # TODO: technically, the following should wait for startService
+ s.myPort.startReading()
</ins><span class="cx"> factory.myServer = s
</span><span class="cx"> return s
</span><span class="cx"> return serverServiceMaker
</span><span class="cx">
</span><span class="cx">
</span><del>- def fillUp(self):
</del><ins>+ def fillUp(self, acknowledged=True, count=0):
</ins><span class="cx"> """
</span><span class="cx"> Fill up all the slots on the connection limiter.
</span><ins>+
+ @param acknowledged: Should the virtual connections created by this
+ method send a message back to the dispatcher indicating that the
+ subprocess has acknowledged receipt of the file descriptor?
+
+ @param count: Amount of load to add; default to the maximum that the
+ limiter.
</ins><span class="cx"> """
</span><del>- for x in range(self.limiter.maxRequests):
</del><ins>+ for x in range(count or self.limiter.maxRequests):
</ins><span class="cx"> self.dispatcher.sendFileDescriptor(None, "SSL")
</span><del>- self.dispatcher.statusMessage(
- self.dispatcher._subprocessSockets[0], "+"
- )
</del><ins>+ if acknowledged:
+ self.dispatcher.statusMessage(
+ self.dispatcher._subprocessSockets[0], "+"
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def processRestart(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoaggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/aggregate.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/aggregate.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/aggregate.py        2013-11-01 22:25:30 UTC (rev 11871)
</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("Not a directory service: %s" % (service,))
</del><ins>+ raise ValueError(
+ "Not a directory service: {0}".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>- "Aggregated services may not vend the same record type: %s"
- % (recordType,)
</del><ins>+ "Aggregated services may not vend "
+ "the same record type: {0}"
+ .format(recordType)
</ins><span class="cx"> )
</span><span class="cx"> recordTypes.add(recordType)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/directory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/directory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/directory.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> fieldName = FieldName
</span><span class="cx">
</span><span class="cx"> normalizedFields = {
</span><del>- FieldName.guid: lambda g: UUID(g).hex,
</del><ins>+ FieldName.guid: lambda g: UUID(g).hex,
</ins><span class="cx"> FieldName.emailAddresses: lambda e: e.lower(),
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -57,9 +57,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __repr__(self):
</span><del>- return "<%s %r>" % (
- self.__class__.__name__,
- self.realmName,
</del><ins>+ return (
+ "<{self.__class__.__name__} {self.realmName!r}>"
+ .format(self=self)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -76,7 +76,9 @@
</span><span class="cx"> the whole directory should be searched.
</span><span class="cx"> @type records: L{set} or L{frozenset}
</span><span class="cx"> """
</span><del>- return fail(QueryNotSupportedError("Unknown expression: %s" % (expression,)))
</del><ins>+ return fail(QueryNotSupportedError(
+ "Unknown expression: {0}".format(expression)
+ ))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -109,7 +111,9 @@
</span><span class="cx"> elif operand == Operand.OR:
</span><span class="cx"> results |= recordsMatchingExpression
</span><span class="cx"> else:
</span><del>- raise QueryNotSupportedError("Unknown operand: %s" % (operand,))
</del><ins>+ raise QueryNotSupportedError(
+ "Unknown operand: {0}".format(operand)
+ )
</ins><span class="cx">
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="lines">@@ -120,12 +124,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,12 +144,15 @@
</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="lines">@@ -168,21 +179,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("%s field is required." % (fieldName,))
</del><ins>+ raise ValueError("{0} field is required.".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("%s field must have at least one value." % (fieldName,))
</del><ins>+ raise ValueError(
+ "{0} field must have at least one value."
+ .format(fieldName)
+ )
</ins><span class="cx"> for value in values:
</span><span class="cx"> if not value:
</span><del>- raise ValueError("%s field must not be empty." % (fieldName,))
</del><ins>+ raise ValueError(
+ "{0} field must not be empty.".format(fieldName)
+ )
</ins><span class="cx">
</span><del>- if fields[FieldName.recordType] not in service.recordType.iterconstants():
- raise ValueError("Record type must be one of %r, not %r." % (
- tuple(service.recordType.iterconstants()),
- fields[FieldName.recordType]
- ))
</del><ins>+ if (
+ fields[FieldName.recordType] not in
+ service.recordType.iterconstants()
+ ):
+ raise ValueError(
+ "Record type must be one of {0!r}, not {1!r}.".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 +218,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 "<%s (%s)%s>" % (
- self.__class__.__name__,
- describe(self.recordType),
- self.shortNames[0],
</del><ins>+ return (
+ "<{self.__class__.__name__} ({recordType}){shortName}>".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 +285,9 @@
</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>+ raise NotImplementedError("Subclasses must implement members()")
</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>+ raise NotImplementedError("Subclasses must implement groups()")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoexpressionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/expression.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/expression.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/expression.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -72,7 +72,11 @@
</span><span class="cx"> @ivar flags: L{NamedConstant} specifying additional options
</span><span class="cx"> """
</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="lines">@@ -85,12 +89,16 @@
</span><span class="cx"> if self.flags is None:
</span><span class="cx"> flags = ""
</span><span class="cx"> else:
</span><del>- flags = " (%s)" % (describe(self.flags),)
</del><ins>+ flags = " ({0})".format(describe(self.flags))
</ins><span class="cx">
</span><del>- return "<%s: %r %s %r%s>" % (
- self.__class__.__name__,
- describe(self.fieldName),
- describe(self.matchType),
- describe(self.fieldValue),
- flags
</del><ins>+ return (
+ "<{self.__class__.__name__}: {fieldName!r} "
+ "{matchType} {fieldValue!r}{flags}>"
+ .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="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoidirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/idirectory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/idirectory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/idirectory.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -51,16 +51,22 @@
</span><span class="cx"> Directory service generic error.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DirectoryConfigurationError(DirectoryServiceError):
</span><span class="cx"> """
</span><span class="cx"> Directory configurtion error.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DirectoryAvailabilityError(DirectoryServiceError):
</span><span class="cx"> """
</span><span class="cx"> Directory not available.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class UnknownRecordTypeError(DirectoryServiceError):
</span><span class="cx"> """
</span><span class="cx"> Unknown record type.
</span><span class="lines">@@ -69,16 +75,22 @@
</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"> """
</span><span class="cx"> Query not supported.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NoSuchRecordError(DirectoryServiceError):
</span><span class="cx"> """
</span><span class="cx"> Record does not exist.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NotAllowedError(DirectoryServiceError):
</span><span class="cx"> """
</span><span class="cx"> Apparently, you can't do that.
</span><span class="lines">@@ -123,6 +135,7 @@
</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><span class="cx"> return getattr(name, "multiValue", False)
</span><span class="lines">@@ -157,106 +170,143 @@
</span><span class="cx"> A directory service may allow support the editing, removal and
</span><span class="cx"> addition of records.
</span><span class="cx"> """
</span><del>- realmName = Attribute("The name of the authentication realm this service represents.")
</del><ins>+ realmName = Attribute(
+ "The name of the authentication realm this service represents."
+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordTypes():
</span><span class="cx"> """
</span><span class="cx"> @return: an iterable of L{NamedConstant}s denoting the record
</span><span class="cx"> types that are kept in this directory.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordsFromExpression(self, expression):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of matching L{IDirectoryRecord}s.
</span><ins>+
</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"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordsFromQuery(expressions, operand=Operand.AND):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of matching L{IDirectoryRecord}s.
</span><ins>+
</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"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordsWithFieldValue(fieldName, value):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordWithUID(uid):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s, or
</span><span class="cx"> C{None} if there is no such record.
</span><span class="cx"> """
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def recordWithGUID(guid):
</span><span class="cx"> """
</span><span class="cx"> Find the record that has the given GUID.
</span><ins>+
</ins><span class="cx"> @param guid: a GUID
</span><span class="cx"> @type guid: L{bytes}
</span><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s, or
</span><span class="cx"> C{None} if there is no such record.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordsWithRecordType(recordType):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordWithShortName(recordType, shortName):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s, or
</span><span class="cx"> C{None} if there is no such record.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def recordsWithEmailAddress(emailAddress):
</span><span class="cx"> """
</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><ins>+
</ins><span class="cx"> @return: a deferred iterable of L{IDirectoryRecord}s, or
</span><span class="cx"> C{None} if there is no such record.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def updateRecords(records, create=False):
</span><span class="cx"> """
</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><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def removeRecords(uids):
</span><span class="cx"> """
</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><span class="cx"> """
</span><span class="lines">@@ -294,6 +344,7 @@
</span><span class="cx"> service = Attribute("The L{IDirectoryService} this record exists in.")
</span><span class="cx"> fields = Attribute("A mapping with L{NamedConstant} keys.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def members():
</span><span class="cx"> """
</span><span class="cx"> Find the records that are members of this group. Only direct
</span><span class="lines">@@ -302,6 +353,7 @@
</span><span class="cx"> direct members of this group.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def groups():
</span><span class="cx"> """
</span><span class="cx"> Find the group records that this record is a member of. Only
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoindexpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/index.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/index.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/index.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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"> """
</span><span class="cx"> Load records.
</span><span class="cx"> """
</span><del>- raise NotImplementedError("Subclasses should implement loadRecords().")
</del><ins>+ raise NotImplementedError("Subclasses must implement loadRecords().")
</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("Unknown query flag: %s" % (describe(flag),))
</del><ins>+ raise NotImplementedError(
+ "Unknown query flag: {0}".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("Unknown match type: %s" % (describe(matchType),))
</del><ins>+ raise NotImplementedError(
+ "Unknown match type: {0}".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("Unknown match type: %s" % (describe(matchType),))
</del><ins>+ raise NotImplementedError(
+ "Unknown match type: {0}".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"> """
</span><span class="cx"> XML directory record
</span><span class="cx"> """
</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="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</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("Name conflict: %r" % (constant.name,))
</del><ins>+ raise ValueError("Name conflict: {0}".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("Multiple values found where one expected.")
</del><ins>+ raise DirectoryServiceError(
+ "Multiple values found where one expected."
+ )
</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, "description", 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, "__iter__"):
</span><span class="cx"> return flags
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwextwhoxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/xml.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/xml.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twext/who/xml.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -144,9 +144,11 @@
</span><span class="cx"> else:
</span><span class="cx"> realmName = repr(realmName)
</span><span class="cx">
</span><del>- return "<%s %s>" % (
- self.__class__.__name__,
- realmName,
</del><ins>+ return (
+ "<{self.__class__.__name__} {realmName}>".format(
+ self=self,
+ realmName=realmName,
+ )
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -201,7 +203,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 +230,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("Incorrect root element: %s" % (directoryNode.tag,))
</del><ins>+ raise ParseError(
+ "Incorrect root element: {0}".format(directoryNode.tag)
+ )
</ins><span class="cx">
</span><del>- realmName = directoryNode.get(self.attribute.realm.value, "").encode("utf-8")
</del><ins>+ realmName = directoryNode.get(
+ self.attribute.realm.value, ""
+ ).encode("utf-8")
</ins><span class="cx">
</span><span class="cx"> if not realmName:
</span><span class="cx"> raise ParseError("No realm name.")
</span><span class="lines">@@ -239,7 +248,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 +288,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, "").encode("utf-8")
</del><ins>+ recordTypeAttribute = recordNode.get(
+ self.attribute.recordType.value, ""
+ ).encode("utf-8")
</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">@@ -357,9 +372,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("Unknown record type: %r" % (value,))
</del><ins>+ raise AssertionError(
+ "Unknown record type: {0}".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 +396,9 @@
</span><span class="cx"> recordNode.append(subNode)
</span><span class="cx">
</span><span class="cx"> else:
</span><del>- raise AssertionError("Unknown field name: %r" % (name,))
</del><ins>+ raise AssertionError(
+ "Unknown field name: {0!r}".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="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavcaldavxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/caldavxml.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/caldavxml.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/caldavxml.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -65,7 +65,11 @@
</span><span class="cx"> "calendar-query-extended",
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+caldav_timezones_by_reference_compliance = (
+ "calendar-no-timezone",
+)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> class CalDAVElement (WebDAVElement):
</span><span class="cx"> """
</span><span class="cx"> CalDAV XML element.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectoryappleopendirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/appleopendirectory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/appleopendirectory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/appleopendirectory.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1378,7 +1378,8 @@
</span><span class="cx"> def buildNestedQueryFromTokens(tokens, mapping):
</span><span class="cx"> """
</span><span class="cx"> Build a DS query espression such that all the tokens must appear in either
</span><del>- the fullName (anywhere) or emailAddresses (at the beginning).
</del><ins>+ the fullName (anywhere), emailAddresses (at the beginning) or record name
+ (at the beginning).
</ins><span class="cx">
</span><span class="cx"> @param tokens: The tokens to search on
</span><span class="cx"> @type tokens: C{list} of C{str}
</span><span class="lines">@@ -1394,6 +1395,7 @@
</span><span class="cx"> fields = [
</span><span class="cx"> ("fullName", dsattributes.eDSContains),
</span><span class="cx"> ("emailAddresses", dsattributes.eDSStartsWith),
</span><ins>+ ("recordName", dsattributes.eDSStartsWith),
</ins><span class="cx"> ]
</span><span class="cx">
</span><span class="cx"> outer = []
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/directory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/directory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/directory.py        2013-11-01 22:25:30 UTC (rev 11871)
</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(("%s#calendar-proxy-write" % (guid,),
- record.externalProxies()))
- assignments.append(("%s#calendar-proxy-read" % (guid,),
- record.externalReadOnlyProxies()))
</del><ins>+ if record.enabledForCalendaring:
+ assignments.append(("%s#calendar-proxy-write" % (guid,),
+ record.externalProxies()))
+ assignments.append(("%s#calendar-proxy-read" % (guid,),
+ record.externalReadOnlyProxies()))
</ins><span class="cx">
</span><span class="cx"> return assignments
</span><span class="cx">
</span><span class="lines">@@ -813,7 +814,7 @@
</span><span class="cx"> # populated the membership cache, and if so, return immediately
</span><span class="cx"> if isPopulated:
</span><span class="cx"> self.log.info("Group membership cache is already populated")
</span><del>- returnValue((fast, 0))
</del><ins>+ returnValue((fast, 0, 0))
</ins><span class="cx">
</span><span class="cx"> # We don't care what others are doing right now, we need to update
</span><span class="cx"> useLock = False
</span><span class="lines">@@ -832,15 +833,21 @@
</span><span class="cx"> else:
</span><span class="cx"> self.log.info("Group membership snapshot file exists: %s" %
</span><span class="cx"> (membershipsCacheFile.path,))
</span><del>- previousMembers = pickle.loads(membershipsCacheFile.getContent())
</del><span class="cx"> callGroupsChanged = True
</span><ins>+ try:
+ previousMembers = pickle.loads(membershipsCacheFile.getContent())
+ except:
+ self.log.warn("Could not parse snapshot; will regenerate cache")
+ fast = False
+ previousMembers = {}
+ callGroupsChanged = False
</ins><span class="cx">
</span><span class="cx"> if useLock:
</span><span class="cx"> self.log.info("Attempting to acquire group membership cache lock")
</span><span class="cx"> acquiredLock = (yield self.cache.acquireLock())
</span><span class="cx"> if not acquiredLock:
</span><span class="cx"> self.log.info("Group membership cache lock held by another process")
</span><del>- returnValue((fast, 0))
</del><ins>+ returnValue((fast, 0, 0))
</ins><span class="cx"> self.log.info("Acquired lock")
</span><span class="cx">
</span><span class="cx"> if not fast and self.useExternalProxies:
</span><span class="lines">@@ -850,7 +857,11 @@
</span><span class="cx"> if extProxyCacheFile.exists():
</span><span class="cx"> self.log.info("External proxies snapshot file exists: %s" %
</span><span class="cx"> (extProxyCacheFile.path,))
</span><del>- previousAssignments = pickle.loads(extProxyCacheFile.getContent())
</del><ins>+ try:
+ previousAssignments = pickle.loads(extProxyCacheFile.getContent())
+ except:
+ self.log.warn("Could not parse external proxies snapshot")
+ previousAssignments = []
</ins><span class="cx">
</span><span class="cx"> if useLock:
</span><span class="cx"> yield self.cache.extendLock()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectoryldapdirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/ldapdirectory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/ldapdirectory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/ldapdirectory.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -391,6 +391,12 @@
</span><span class="cx">
</span><span class="cx"> # Build filter
</span><span class="cx"> filterstr = "(|(%s=*)(%s=*))" % (readAttr, writeAttr)
</span><ins>+ # ...taking into account only calendar-enabled records
+ enabledAttr = self.rdnSchema["locations"]["calendarEnabledAttr"]
+ enabledValue = self.rdnSchema["locations"]["calendarEnabledValue"]
+ if enabledAttr and enabledValue:
+ filterstr = "(&(%s=%s)%s)" % (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">@@ -1046,7 +1052,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("Got LDAP record %s" % (record,))
</del><ins>+ self.log.debug("Got LDAP record {rec}", rec=record)
</ins><span class="cx">
</span><span class="cx"> if not unrestricted:
</span><span class="cx"> self.log.debug("%s is not enabled because it's not a member of group: %s" % (dn, self.restrictToGroup))
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorytesttest_buildquerypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_buildquery.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_buildquery.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_buildquery.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -140,17 +140,17 @@
</span><span class="cx"> query = buildNestedQueryFromTokens(["foo"], OpenDirectoryService._ODFields)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> query.generate(),
</span><del>- "(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*))"
</del><ins>+ "(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*)(dsAttrTypeStandard:RecordName=foo*))"
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> query = buildNestedQueryFromTokens(["foo", "bar"], OpenDirectoryService._ODFields)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> query.generate(),
</span><del>- "(&(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*))(|(dsAttrTypeStandard:RealName=*bar*)(dsAttrTypeStandard:EMailAddress=bar*)))"
</del><ins>+ "(&(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*)(dsAttrTypeStandard:RecordName=foo*))(|(dsAttrTypeStandard:RealName=*bar*)(dsAttrTypeStandard:EMailAddress=bar*)(dsAttrTypeStandard:RecordName=bar*)))"
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> query = buildNestedQueryFromTokens(["foo", "bar", "baz"], OpenDirectoryService._ODFields)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> query.generate(),
</span><del>- "(&(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*))(|(dsAttrTypeStandard:RealName=*bar*)(dsAttrTypeStandard:EMailAddress=bar*))(|(dsAttrTypeStandard:RealName=*baz*)(dsAttrTypeStandard:EMailAddress=baz*)))"
</del><ins>+ "(&(|(dsAttrTypeStandard:RealName=*foo*)(dsAttrTypeStandard:EMailAddress=foo*)(dsAttrTypeStandard:RecordName=foo*))(|(dsAttrTypeStandard:RealName=*bar*)(dsAttrTypeStandard:EMailAddress=bar*)(dsAttrTypeStandard:RecordName=bar*))(|(dsAttrTypeStandard:RealName=*baz*)(dsAttrTypeStandard:EMailAddress=baz*)(dsAttrTypeStandard:RecordName=baz*)))"
</ins><span class="cx"> )
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavdirectorytesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_directory.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_directory.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/directory/test/test_directory.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -243,7 +243,7 @@
</span><span class="cx"> # Prevent an update by locking the cache
</span><span class="cx"> acquiredLock = (yield cache.acquireLock())
</span><span class="cx"> self.assertTrue(acquiredLock)
</span><del>- self.assertEquals((False, 0), (yield updater.updateCache()))
</del><ins>+ self.assertEquals((False, 0, 0), (yield updater.updateCache()))
</ins><span class="cx">
</span><span class="cx"> # You can't lock when already locked:
</span><span class="cx"> acquiredLockAgain = (yield cache.acquireLock())
</span><span class="lines">@@ -540,7 +540,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: "transporter" is now gone for everyone
+
+ ("wsanchez",
+ set(["mercury", "apollo", "orion", "gemini"]),
+ set(["non_calendar_proxy"]),
+ set(['left_coast',
+ 'both_coasts',
+ 'recursive1_coasts',
+ 'recursive2_coasts',
+ 'gemini#calendar-proxy-write',
+ ]),
+ ),
+ ("cdaboo",
+ set(["apollo", "orion", "non_calendar_proxy"]),
+ set(["non_calendar_proxy"]),
+ set(['both_coasts',
+ 'non_calendar_group',
+ 'recursive1_coasts',
+ 'recursive2_coasts',
+ ]),
+ ),
+ ("lecroy",
+ set(["apollo", "mercury", "non_calendar_proxy"]),
+ 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 [
+ (
+ "transporter#calendar-proxy-write",
+ set(["8B4288F6-CC82-491D-8EF9-642EF4F3E7D0"])
+ ),
+ ]
+
+ 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)
+
+ ("wsanchez",
+ set(["mercury", "apollo", "orion", "gemini"]),
+ set(["non_calendar_proxy"]),
+ set(['left_coast',
+ 'both_coasts',
+ 'recursive1_coasts',
+ 'recursive2_coasts',
+ 'gemini#calendar-proxy-write',
+ ]),
+ ),
+ ("cdaboo",
+ set(["apollo", "orion", "non_calendar_proxy"]),
+ set(["non_calendar_proxy"]),
+ set(['both_coasts',
+ 'non_calendar_group',
+ 'recursive1_coasts',
+ 'recursive2_coasts',
+ ]),
+ ),
+ ("lecroy",
+ set(["apollo", "mercury", "non_calendar_proxy", "transporter"]),
+ 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"> """
</span><span class="cx"> Ensure external proxy assignment diffing works
</span><span class="lines">@@ -667,7 +827,7 @@
</span><span class="cx"> # as indicated by the return value for "fast". Note that the cache
</span><span class="cx"> # is already populated so updateCache( ) in fast mode will not do
</span><span class="cx"> # anything, and numMembers will be 0.
</span><del>- fast, numMembers = (yield updater.updateCache(fast=True))
</del><ins>+ fast, numMembers, numChanged = (yield updater.updateCache(fast=True))
</ins><span class="cx"> self.assertEquals(fast, True)
</span><span class="cx"> self.assertEquals(numMembers, 0)
</span><span class="cx">
</span><span class="lines">@@ -678,61 +838,70 @@
</span><span class="cx"> self.assertEquals(numChanged, 0)
</span><span class="cx">
</span><span class="cx"> # Verify the snapshot contains the pickled dictionary we expect
</span><ins>+ expected = {
+ "46D9D716-CBEE-490F-907A-66FA6C3767FF":
+ set([
+ u"00599DAF-3E75-42DD-9DB7-52617E79943F",
+ ]),
+ "5A985493-EE2C-4665-94CF-4DFEA3A89500":
+ set([
+ u"non_calendar_group",
+ u"recursive1_coasts",
+ u"recursive2_coasts",
+ u"both_coasts"
+ ]),
+ "6423F94A-6B76-4A3A-815B-D52CFD77935D":
+ set([
+ u"left_coast",
+ u"recursive1_coasts",
+ u"recursive2_coasts",
+ u"both_coasts"
+ ]),
+ "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1":
+ set([
+ u"left_coast",
+ u"both_coasts"
+ ]),
+ "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0":
+ set([
+ u"non_calendar_group",
+ u"left_coast",
+ u"both_coasts"
+ ]),
+ "left_coast":
+ set([
+ u"both_coasts"
+ ]),
+ "recursive1_coasts":
+ set([
+ u"recursive1_coasts",
+ u"recursive2_coasts"
+ ]),
+ "recursive2_coasts":
+ set([
+ u"recursive1_coasts",
+ u"recursive2_coasts"
+ ]),
+ "right_coast":
+ set([
+ u"both_coasts"
+ ])
+ }
</ins><span class="cx"> members = pickle.loads(snapshotFile.getContent())
</span><del>- self.assertEquals(
- members,
- {
- "46D9D716-CBEE-490F-907A-66FA6C3767FF":
- set([
- u"00599DAF-3E75-42DD-9DB7-52617E79943F",
- ]),
- "5A985493-EE2C-4665-94CF-4DFEA3A89500":
- set([
- u"non_calendar_group",
- u"recursive1_coasts",
- u"recursive2_coasts",
- u"both_coasts"
- ]),
- "6423F94A-6B76-4A3A-815B-D52CFD77935D":
- set([
- u"left_coast",
- u"recursive1_coasts",
- u"recursive2_coasts",
- u"both_coasts"
- ]),
- "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1":
- set([
- u"left_coast",
- u"both_coasts"
- ]),
- "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0":
- set([
- u"non_calendar_group",
- u"left_coast",
- u"both_coasts"
- ]),
- "left_coast":
- set([
- u"both_coasts"
- ]),
- "recursive1_coasts":
- set([
- u"recursive1_coasts",
- u"recursive2_coasts"
- ]),
- "recursive2_coasts":
- set([
- u"recursive1_coasts",
- u"recursive2_coasts"
- ]),
- "right_coast":
- set([
- u"both_coasts"
- ])
- }
- )
</del><ins>+ self.assertEquals(members, expected)
+
+ # "Corrupt" the snapshot and verify it is regenerated properly
+ snapshotFile.setContent("xyzzy")
+ cache.delete("group-cacher-populated")
+ fast, numMembers, numChanged = (yield updater.updateCache(fast=True))
+ self.assertEquals(fast, False)
+ self.assertEquals(numMembers, 9)
+ self.assertEquals(numChanged, 9)
+ self.assertTrue(snapshotFile.exists())
+ members = pickle.loads(snapshotFile.getContent())
+ self.assertEquals(members, expected)
+
</ins><span class="cx">
</span><del>-
</del><span class="cx"> def test_autoAcceptMembers(self):
</span><span class="cx"> """
</span><span class="cx"> autoAcceptMembers( ) returns an empty list if no autoAcceptGroup is
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavmethodreport_sync_collectionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/method/report_sync_collection.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/method/report_sync_collection.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/method/report_sync_collection.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -58,6 +58,14 @@
</span><span class="cx">
</span><span class="cx"> responses = []
</span><span class="cx">
</span><ins>+ # Do not support limit
+ if sync_collection.sync_limit is not None:
+ raise HTTPError(ErrorResponse(
+ responsecode.INSUFFICIENT_STORAGE_SPACE,
+ element.NumberOfMatchesWithinLimits(),
+ "Report limit not supported",
+ ))
+
</ins><span class="cx"> # Process Depth and sync-level for backwards compatibility
</span><span class="cx"> # Use sync-level if present and ignore Depth, else use Depth
</span><span class="cx"> if sync_collection.sync_level:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/resource.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/resource.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/resource.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -324,7 +324,7 @@
</span><span class="cx"> @param transaction: optional transaction to use instead of associated transaction
</span><span class="cx"> @type transaction: L{txdav.caldav.idav.ITransaction}
</span><span class="cx"> """
</span><del>- result = yield super(CalDAVResource, self).renderHTTP(request)
</del><ins>+ response = yield super(CalDAVResource, self).renderHTTP(request)
</ins><span class="cx"> if transaction is None:
</span><span class="cx"> transaction = self._associatedTransaction
</span><span class="cx"> if transaction is not None:
</span><span class="lines">@@ -332,9 +332,19 @@
</span><span class="cx"> yield transaction.abort()
</span><span class="cx"> else:
</span><span class="cx"> yield transaction.commit()
</span><del>- returnValue(result)
</del><span class="cx">
</span><ins>+ # Log extended item
+ if transaction.logItems:
+ if not hasattr(request, "extendedLogItems"):
+ request.extendedLogItems = {}
+ request.extendedLogItems.update(transaction.logItems)
</ins><span class="cx">
</span><ins>+ # May need to reset the last-modified header in the response as txn.commit() can change it due to pre-commit hooks
+ if response.headers.hasHeader("last-modified"):
+ response.headers.setHeader("last-modified", self.lastModified())
+ returnValue(response)
+
+
</ins><span class="cx"> # Begin transitional new-store resource interface:
</span><span class="cx">
</span><span class="cx"> def copyDeadPropertiesTo(self, other):
</span><span class="lines">@@ -2547,15 +2557,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):
- """
- Return all child object resources with the specified UID.
-
- Pass through direct to store.
- """
- 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="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/scheduling_store/caldav/resource.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/scheduling_store/caldav/resource.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/scheduling_store/caldav/resource.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -422,8 +422,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, "extendedLogItems"):
+ 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="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/stdconfig.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/stdconfig.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/stdconfig.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -54,7 +54,7 @@
</span><span class="cx"> },
</span><span class="cx"> "twistedcaldav.directory.appleopendirectory.OpenDirectoryService": {
</span><span class="cx"> "node": "/Search",
</span><del>- "cacheTimeout": 10, # Minutes
</del><ins>+ "cacheTimeout": 1, # Minutes
</ins><span class="cx"> "batchSize": 100, # for splitting up large queries
</span><span class="cx"> "negativeCaching": False,
</span><span class="cx"> "restrictEnabledRecords": False,
</span><span class="lines">@@ -62,7 +62,7 @@
</span><span class="cx"> "recordTypes": ("users", "groups"),
</span><span class="cx"> },
</span><span class="cx"> "twistedcaldav.directory.ldapdirectory.LdapDirectoryService": {
</span><del>- "cacheTimeout": 10, # Minutes
</del><ins>+ "cacheTimeout": 1, # Minutes
</ins><span class="cx"> "negativeCaching": False,
</span><span class="cx"> "warningThresholdSeconds": 3,
</span><span class="cx"> "batchSize": 500, # for splitting up large queries
</span><span class="lines">@@ -307,9 +307,15 @@
</span><span class="cx"> "FailIfUpgradeNeeded" : 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>- "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists
- # in ConfigRoot, stop the service after finishing upgrade phase
</del><ins>+ "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # if this file exists in ConfigRoot, stop
+ # the service after finishing upgrade phase
</ins><span class="cx">
</span><ins>+ "UpgradeHomePrefix" : "", # 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">@@ -449,6 +455,7 @@
</span><span class="cx"> #
</span><span class="cx"> "AccessLogFile" : "access.log", # Apache-style access log
</span><span class="cx"> "ErrorLogFile" : "error.log", # Server activity log
</span><ins>+ "AgentLogFile" : "agent.log", # Agent activity log
</ins><span class="cx"> "ErrorLogEnabled" : True, # True = use log file, False = stdout
</span><span class="cx"> "ErrorLogRotateMB" : 10, # Rotate error log after so many megabytes
</span><span class="cx"> "ErrorLogMaxRotatedFiles" : 5, # Retain this many error log files
</span><span class="lines">@@ -563,8 +570,8 @@
</span><span class="cx"> }
</span><span class="cx"> },
</span><span class="cx">
</span><del>- "EnableTimezonesByReference" : False, # Strip out VTIMEZONES that are known
- "UsePackageTimezones" : False, # Use timezone data from twistedcaldav.zoneinfo - don't copy to Data directory
</del><ins>+ "EnableTimezonesByReference" : True, # Strip out VTIMEZONES that are known
+ "UsePackageTimezones" : False, # Use timezone data from twistedcaldav.zoneinfo - don't copy to Data directory
</ins><span class="cx">
</span><span class="cx"> "EnableBatchUpload" : True, # POST batch uploads
</span><span class="cx"> "MaxResourcesBatchUpload" : 100, # Maximum number of resources in a batch POST
</span><span class="lines">@@ -825,7 +832,12 @@
</span><span class="cx"> # connections used per worker process.
</span><span class="cx">
</span><span class="cx"> "ListenBacklog": 2024,
</span><del>- "IdleConnectionTimeOut": 15,
</del><ins>+
+ "IncomingDataTimeOut": 60, # Max. time between request lines
+ "PipelineIdleTimeOut": 15, # Max. time between pipelined requests
+ "IdleConnectionTimeOut": 60 * 6, # Max. time for response processing
+ "CloseConnectionTimeOut": 15, # Max. time for client close
+
</ins><span class="cx"> "UIDReservationTimeOut": 30 * 60,
</span><span class="cx">
</span><span class="cx"> "MaxMultigetWithDataHrefs": 5000,
</span><span class="lines">@@ -996,6 +1008,10 @@
</span><span class="cx"> # America/Los_Angeles.
</span><span class="cx"> "DefaultTimezone" : "",
</span><span class="cx">
</span><ins>+ # After this many seconds of no admin requests, shutdown the agent. Zero
+ # means no automatic shutdown.
+ "AgentInactivityTimeoutSeconds" : 4 * 60 * 60,
+
</ins><span class="cx"> # These two aren't relative to ConfigRoot:
</span><span class="cx"> "Includes": [], # Other plists to parse after this one
</span><span class="cx"> "WritableConfigFile" : "", # which config file calendarserver_config should
</span><span class="lines">@@ -1082,6 +1098,7 @@
</span><span class="cx"> ("ConfigRoot", ("Scheduling", "iSchedule", "DKIM", "PrivateExchanges",)),
</span><span class="cx"> ("LogRoot", "AccessLogFile"),
</span><span class="cx"> ("LogRoot", "ErrorLogFile"),
</span><ins>+ ("LogRoot", "AgentLogFile"),
</ins><span class="cx"> ("LogRoot", ("Postgres", "LogFile",)),
</span><span class="cx"> ("LogRoot", ("LogDatabase", "StatisticsLogFile",)),
</span><span class="cx"> ("LogRoot", "AccountingLogRoot"),
</span><span class="lines">@@ -1537,6 +1554,8 @@
</span><span class="cx"> compliance += caldavxml.caldav_managed_attachments_compliance
</span><span class="cx"> if configDict.Scheduling.Options.TimestampAttendeePartStatChanges:
</span><span class="cx"> compliance += customxml.calendarserver_partstat_changes_compliance
</span><ins>+ if configDict.EnableTimezonesByReference:
+ compliance += caldavxml.caldav_timezones_by_reference_compliance
</ins><span class="cx"> else:
</span><span class="cx"> compliance = ()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/storebridge.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/storebridge.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/storebridge.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -77,7 +77,7 @@
</span><span class="cx"> import hashlib
</span><span class="cx"> import time
</span><span class="cx"> import uuid
</span><del>-from twext.web2 import responsecode
</del><ins>+from twext.web2 import responsecode, http_headers, http
</ins><span class="cx"> from twext.web2.iweb import IResponse
</span><span class="cx"> from twistedcaldav.customxml import calendarserver_namespace
</span><span class="cx"> from twistedcaldav.instance import InvalidOverriddenInstanceError, \
</span><span class="lines">@@ -2222,6 +2222,41 @@
</span><span class="cx"> response.headers.setHeader("content-type", self.contentType())
</span><span class="cx"> returnValue(response)
</span><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def checkPreconditions(self, request):
+ """
+ We override the base class to trap the failure case and process any Prefer header.
+ """
+
+ try:
+ response = yield super(_CommonObjectResource, self).checkPreconditions(request)
+ except HTTPError as e:
+ if e.response.code == responsecode.PRECONDITION_FAILED:
+ response = yield self._processPrefer(request, e.response)
+ raise HTTPError(response)
+ else:
+ raise
+
+ returnValue(response)
+
+
+ @inlineCallbacks
+ def _processPrefer(self, request, response):
+ # Look for Prefer header
+ prefer = request.headers.getHeader("prefer", {})
+ returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
+
+ if returnRepresentation and (response.code / 100 == 2 or response.code == responsecode.PRECONDITION_FAILED):
+ oldcode = response.code
+ response = (yield self.http_GET(request))
+ if oldcode in (responsecode.CREATED, responsecode.PRECONDITION_FAILED):
+ response.code = oldcode
+ response.headers.removeHeader("content-location")
+ response.headers.setHeader("content-location", self.url())
+
+ returnValue(response)
+
</ins><span class="cx"> # The following are used to map store exceptions into HTTP error responses
</span><span class="cx"> StoreExceptionsStatusErrors = set()
</span><span class="cx"> StoreExceptionsErrors = {}
</span><span class="lines">@@ -2601,7 +2636,76 @@
</span><span class="cx"> AttachmentRemoveFailed: (caldav_namespace, "valid-attachment-remove",),
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @inlineCallbacks
</span><ins>+ def _checkPreconditions(self, request):
+ """
+ We override the base class to handle the special implicit scheduling weak ETag behavior
+ for compatibility with old clients using If-Match.
+ """
+
+ if config.Scheduling.CalDAV.ScheduleTagCompatibility:
+
+ if self.exists():
+ etags = self.scheduleEtags
+ if len(etags) > 1:
+ # This is almost verbatim from twext.web2.static.checkPreconditions
+ if request.method not in ("GET", "HEAD"):
+
+ # Always test against the current etag first just in case schedule-etags is out of sync
+ etag = (yield self.etag())
+ etags = (etag,) + tuple([http_headers.ETag(schedule_etag) for schedule_etag in etags])
+
+ # Loop over each tag and succeed if any one matches, else re-raise last exception
+ exists = self.exists()
+ last_modified = self.lastModified()
+ last_exception = None
+ for etag in etags:
+ try:
+ http.checkPreconditions(
+ request,
+ entityExists=exists,
+ etag=etag,
+ lastModified=last_modified,
+ )
+ except HTTPError, e:
+ last_exception = e
+ else:
+ break
+ else:
+ if last_exception:
+ raise last_exception
+
+ # Check per-method preconditions
+ method = getattr(self, "preconditions_" + request.method, None)
+ if method:
+ returnValue((yield method(request)))
+ else:
+ returnValue(None)
+
+ result = (yield super(CalendarObjectResource, self).checkPreconditions(request))
+ returnValue(result)
+
+
+ @inlineCallbacks
+ def checkPreconditions(self, request):
+ """
+ We override the base class to do special schedule tag processing.
+ """
+
+ try:
+ response = yield self._checkPreconditions(request)
+ except HTTPError as e:
+ if e.response.code == responsecode.PRECONDITION_FAILED:
+ response = yield self._processPrefer(request, e.response)
+ raise HTTPError(response)
+ else:
+ raise
+
+ returnValue(response)
+
+
+ @inlineCallbacks
</ins><span class="cx"> def http_PUT(self, request):
</span><span class="cx">
</span><span class="cx"> # Content-type check
</span><span class="lines">@@ -2615,7 +2719,14 @@
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> # Do schedule tag check
</span><del>- schedule_tag_match = self.validIfScheduleMatch(request)
</del><ins>+ try:
+ schedule_tag_match = self.validIfScheduleMatch(request)
+ except HTTPError as e:
+ if e.response.code == responsecode.PRECONDITION_FAILED:
+ response = yield self._processPrefer(request, e.response)
+ raise HTTPError(response)
+ else:
+ raise
</ins><span class="cx">
</span><span class="cx"> # Read the calendar component from the stream
</span><span class="cx"> try:
</span><span class="lines">@@ -2681,18 +2792,9 @@
</span><span class="cx">
</span><span class="cx"> request.addResponseFilter(_removeEtag, atEnd=True)
</span><span class="cx">
</span><del>- # Look for Prefer header
- prefer = request.headers.getHeader("prefer", {})
- returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
</del><ins>+ # Handle Prefer header
+ response = yield self._processPrefer(request, response)
</ins><span class="cx">
</span><del>- if returnRepresentation and response.code / 100 == 2:
- oldcode = response.code
- response = (yield self.http_GET(request))
- if oldcode == responsecode.CREATED:
- response.code = responsecode.CREATED
- response.headers.removeHeader("content-location")
- response.headers.setHeader("content-location", self.url())
-
</del><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx"> # Handle the various store errors
</span><span class="lines">@@ -2871,18 +2973,12 @@
</span><span class="cx"> raise
</span><span class="cx">
</span><span class="cx"> # Look for Prefer header
</span><del>- prefer = request.headers.getHeader("prefer", {})
- returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
- if returnRepresentation:
- result = (yield self.render(request))
- result.code = OK
- result.headers.removeHeader("content-location")
- result.headers.setHeader("content-location", request.path)
- else:
- result = post_result
</del><ins>+ result = yield self._processPrefer(request, post_result)
+
</ins><span class="cx"> if action in ("attachment-add", "attachment-update",):
</span><span class="cx"> result.headers.setHeader("location", location)
</span><span class="cx"> result.headers.addRawHeader("Cal-Managed-ID", attachment.managedID())
</span><ins>+
</ins><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -3313,17 +3409,8 @@
</span><span class="cx"> request.addResponseFilter(_removeEtag, atEnd=True)
</span><span class="cx">
</span><span class="cx"> # Look for Prefer header
</span><del>- prefer = request.headers.getHeader("prefer", {})
- returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
</del><ins>+ response = yield self._processPrefer(request, response)
</ins><span class="cx">
</span><del>- if returnRepresentation and response.code / 100 == 2:
- oldcode = response.code
- response = (yield self.http_GET(request))
- if oldcode == responsecode.CREATED:
- response.code = responsecode.CREATED
- response.headers.removeHeader("content-location")
- response.headers.setHeader("content-location", self.url())
-
</del><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx"> # Handle the various store errors
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAfricaJubaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Africa/Juba.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Africa/Juba.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Africa/Juba.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,7 +9,7 @@
</span><span class="cx"> DTSTART:19310101T000000
</span><span class="cx"> RDATE:19310101T000000
</span><span class="cx"> TZNAME:CAST
</span><del>-TZOFFSETFROM:+020624
</del><ins>+TZOFFSETFROM:+021008
</ins><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaAnguillaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Anguilla.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Anguilla.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Anguilla.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,7 +9,7 @@
</span><span class="cx"> DTSTART:19120302T000000
</span><span class="cx"> RDATE:19120302T000000
</span><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-041216
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaAraguainaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Araguaina.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Araguaina.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Araguaina.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> RDATE:19981011T000000
</span><span class="cx"> RDATE:19991003T000000
</span><span class="cx"> RDATE:20021103T000000
</span><ins>+RDATE:20121021T000000
</ins><span class="cx"> TZNAME:BRST
</span><span class="cx"> TZOFFSETFROM:-0300
</span><span class="cx"> TZOFFSETTO:-0200
</span><span class="lines">@@ -64,7 +65,7 @@
</span><span class="cx"> RDATE:19980301T000000
</span><span class="cx"> RDATE:19990221T000000
</span><span class="cx"> RDATE:20000227T000000
</span><del>-RDATE:20150222T000000
</del><ins>+RDATE:20130217T000000
</ins><span class="cx"> TZNAME:BRT
</span><span class="cx"> TZOFFSETFROM:-0200
</span><span class="cx"> TZOFFSETTO:-0300
</span><span class="lines">@@ -94,6 +95,7 @@
</span><span class="cx"> DTSTART:19900917T000000
</span><span class="cx"> RDATE:19900917T000000
</span><span class="cx"> RDATE:20030924T000000
</span><ins>+RDATE:20130901T000000
</ins><span class="cx"> TZNAME:BRT
</span><span class="cx"> TZOFFSETFROM:-0300
</span><span class="cx"> TZOFFSETTO:-0300
</span><span class="lines">@@ -119,26 +121,5 @@
</span><span class="cx"> TZOFFSETFROM:-0200
</span><span class="cx"> TZOFFSETTO:-0300
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20121021T000000
-RRULE:FREQ=YEARLY;BYDAY=3SU;BYMONTH=10
-TZNAME:BRST
-TZOFFSETFROM:-0300
-TZOFFSETTO:-0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20130217T000000
-RRULE:FREQ=YEARLY;UNTIL=20140216T020000Z;BYDAY=3SU;BYMONTH=2
-TZNAME:BRT
-TZOFFSETFROM:-0200
-TZOFFSETTO:-0300
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:20160221T000000
-RRULE:FREQ=YEARLY;UNTIL=20220220T020000Z;BYDAY=3SU;BYMONTH=2
-TZNAME:BRT
-TZOFFSETFROM:-0200
-TZOFFSETTO:-0300
-END:STANDARD
</del><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaArgentinaSan_Luisics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Argentina/San_Luis.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Argentina/San_Luis.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Argentina/San_Luis.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -144,6 +144,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19910601T000000
</span><span class="cx"> RDATE:19910601T000000
</span><ins>+RDATE:20091011T000000
</ins><span class="cx"> TZNAME:ART
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0300
</span><span class="lines">@@ -178,7 +179,7 @@
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span><span class="cx"> DTSTART:20081012T000000
</span><del>-RRULE:FREQ=YEARLY;UNTIL=20091011T040000Z;BYDAY=2SU;BYMONTH=10
</del><ins>+RDATE:20081012T000000
</ins><span class="cx"> TZNAME:WARST
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0300
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaArubaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Aruba.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Aruba.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Aruba.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,7 +9,7 @@
</span><span class="cx"> DTSTART:19120212T000000
</span><span class="cx"> RDATE:19120212T000000
</span><span class="cx"> TZNAME:ANT
</span><del>-TZOFFSETFROM:-044024
</del><ins>+TZOFFSETFROM:-043547
</ins><span class="cx"> TZOFFSETTO:-0430
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaCaymanics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Cayman.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Cayman.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Cayman.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -10,13 +10,13 @@
</span><span class="cx"> RDATE:18900101T000000
</span><span class="cx"> TZNAME:KMT
</span><span class="cx"> TZOFFSETFROM:-052532
</span><del>-TZOFFSETTO:-050712
</del><ins>+TZOFFSETTO:-050711
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19120201T000000
</span><span class="cx"> RDATE:19120201T000000
</span><span class="cx"> TZNAME:EST
</span><del>-TZOFFSETFROM:-050712
</del><ins>+TZOFFSETFROM:-050711
</ins><span class="cx"> TZOFFSETTO:-0500
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaDominicaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Dominica.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Dominica.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Dominica.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Dominica
</span><span class="cx"> X-LIC-LOCATION:America/Dominica
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000100
-RDATE:19110701T000100
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040536
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGrand_Turkics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grand_Turk.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grand_Turk.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grand_Turk.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -10,13 +10,13 @@
</span><span class="cx"> RDATE:18900101T000000
</span><span class="cx"> TZNAME:KMT
</span><span class="cx"> TZOFFSETFROM:-044432
</span><del>-TZOFFSETTO:-050712
</del><ins>+TZOFFSETTO:-050711
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19120201T000000
</span><span class="cx"> RDATE:19120201T000000
</span><span class="cx"> TZNAME:EST
</span><del>-TZOFFSETFROM:-050712
</del><ins>+TZOFFSETFROM:-050711
</ins><span class="cx"> TZOFFSETTO:-0500
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGrenadaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grenada.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grenada.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Grenada.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Grenada
</span><span class="cx"> X-LIC-LOCATION:America/Grenada
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000000
-RDATE:19110701T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-0407
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaGuadeloupeics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Guadeloupe.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Guadeloupe.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Guadeloupe.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Guadeloupe
</span><span class="cx"> X-LIC-LOCATION:America/Guadeloupe
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110608T000000
-RDATE:19110608T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040608
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaJamaicaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Jamaica.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Jamaica.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Jamaica.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,14 +9,14 @@
</span><span class="cx"> DTSTART:18900101T000000
</span><span class="cx"> RDATE:18900101T000000
</span><span class="cx"> TZNAME:KMT
</span><del>-TZOFFSETFROM:-050712
-TZOFFSETTO:-050712
</del><ins>+TZOFFSETFROM:-050711
+TZOFFSETTO:-050711
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19120201T000000
</span><span class="cx"> RDATE:19120201T000000
</span><span class="cx"> TZNAME:EST
</span><del>-TZOFFSETFROM:-050712
</del><ins>+TZOFFSETFROM:-050711
</ins><span class="cx"> TZOFFSETTO:-0500
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaMarigotics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Marigot.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Marigot.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Marigot.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Marigot
</span><span class="cx"> X-LIC-LOCATION:America/Marigot
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110608T000000
-RDATE:19110608T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040608
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaMontserratics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Montserrat.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Montserrat.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Montserrat.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Montserrat
</span><span class="cx"> X-LIC-LOCATION:America/Montserrat
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000100
-RDATE:19110701T000100
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040852
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Barthelemyics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Barthelemy.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Barthelemy.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Barthelemy.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/St_Barthelemy
</span><span class="cx"> X-LIC-LOCATION:America/St_Barthelemy
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110608T000000
-RDATE:19110608T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040608
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Kittsics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Kitts.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Kitts.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Kitts.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,7 +9,7 @@
</span><span class="cx"> DTSTART:19120302T000000
</span><span class="cx"> RDATE:19120302T000000
</span><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-041052
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Luciaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Lucia.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Lucia.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Lucia.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,17 +6,10 @@
</span><span class="cx"> TZID:America/St_Lucia
</span><span class="cx"> X-LIC-LOCATION:America/St_Lucia
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:18900101T000000
-RDATE:18900101T000000
-TZNAME:CMT
-TZOFFSETFROM:-0404
-TZOFFSETTO:-0404
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19120101T000000
-RDATE:19120101T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-0404
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Thomasics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Thomas.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Thomas.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Thomas.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/St_Thomas
</span><span class="cx"> X-LIC-LOCATION:America/St_Thomas
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000000
-RDATE:19110701T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-041944
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaSt_Vincentics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Vincent.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Vincent.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/St_Vincent.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,17 +6,10 @@
</span><span class="cx"> TZID:America/St_Vincent
</span><span class="cx"> X-LIC-LOCATION:America/St_Vincent
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:18900101T000000
-RDATE:18900101T000000
-TZNAME:KMT
-TZOFFSETFROM:-040456
-TZOFFSETTO:-040456
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19120101T000000
-RDATE:19120101T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-040456
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaTortolaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Tortola.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Tortola.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Tortola.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Tortola
</span><span class="cx"> X-LIC-LOCATION:America/Tortola
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000000
-RDATE:19110701T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-041828
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAmericaVirginics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Virgin.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Virgin.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/America/Virgin.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,10 @@
</span><span class="cx"> TZID:America/Virgin
</span><span class="cx"> X-LIC-LOCATION:America/Virgin
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19110701T000000
-RDATE:19110701T000000
</del><ins>+DTSTART:19120302T000000
+RDATE:19120302T000000
</ins><span class="cx"> TZNAME:AST
</span><del>-TZOFFSETFROM:-041944
</del><ins>+TZOFFSETFROM:-040604
</ins><span class="cx"> TZOFFSETTO:-0400
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAntarcticaMcMurdoics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/McMurdo.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/McMurdo.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/McMurdo.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,13 +6,62 @@
</span><span class="cx"> TZID:Antarctica/McMurdo
</span><span class="cx"> X-LIC-LOCATION:Antarctica/McMurdo
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19560101T000000
-RDATE:19560101T000000
</del><ins>+DTSTART:18681102T000000
+RDATE:18681102T000000
</ins><span class="cx"> TZNAME:NZST
</span><del>-TZOFFSETFROM:+0000
</del><ins>+TZOFFSETFROM:+113904
+TZOFFSETTO:+1130
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19271106T020000
+RDATE:19271106T020000
+TZNAME:NZST
+TZOFFSETFROM:+1130
+TZOFFSETTO:+1230
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19280304T020000
+RDATE:19280304T020000
+TZNAME:NZMT
+TZOFFSETFROM:+1230
+TZOFFSETTO:+1130
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19281014T020000
+RRULE:FREQ=YEARLY;UNTIL=19331007T143000Z;BYDAY=2SU;BYMONTH=10
+TZNAME:NZST
+TZOFFSETFROM:+1130
</ins><span class="cx"> TZOFFSETTO:+1200
</span><ins>+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19290317T020000
+RRULE:FREQ=YEARLY;UNTIL=19330318T140000Z;BYDAY=3SU;BYMONTH=3
+TZNAME:NZMT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1130
</ins><span class="cx"> END:STANDARD
</span><ins>+BEGIN:STANDARD
+DTSTART:19340429T020000
+RRULE:FREQ=YEARLY;UNTIL=19400427T140000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:NZMT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1130
+END:STANDARD
</ins><span class="cx"> BEGIN:DAYLIGHT
</span><ins>+DTSTART:19340930T020000
+RRULE:FREQ=YEARLY;UNTIL=19400928T143000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:NZST
+TZOFFSETFROM:+1130
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19460101T000000
+RDATE:19460101T000000
+TZNAME:NZST
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1200
+END:STANDARD
+BEGIN:DAYLIGHT
</ins><span class="cx"> DTSTART:19741103T020000
</span><span class="cx"> RDATE:19741103T020000
</span><span class="cx"> RDATE:19891008T020000
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAntarcticaSouth_Poleics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/South_Pole.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/South_Pole.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Antarctica/South_Pole.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,13 +6,62 @@
</span><span class="cx"> TZID:Antarctica/South_Pole
</span><span class="cx"> X-LIC-LOCATION:Antarctica/South_Pole
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:19560101T000000
-RDATE:19560101T000000
</del><ins>+DTSTART:18681102T000000
+RDATE:18681102T000000
</ins><span class="cx"> TZNAME:NZST
</span><del>-TZOFFSETFROM:+0000
</del><ins>+TZOFFSETFROM:+113904
+TZOFFSETTO:+1130
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19271106T020000
+RDATE:19271106T020000
+TZNAME:NZST
+TZOFFSETFROM:+1130
+TZOFFSETTO:+1230
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19280304T020000
+RDATE:19280304T020000
+TZNAME:NZMT
+TZOFFSETFROM:+1230
+TZOFFSETTO:+1130
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19281014T020000
+RRULE:FREQ=YEARLY;UNTIL=19331007T143000Z;BYDAY=2SU;BYMONTH=10
+TZNAME:NZST
+TZOFFSETFROM:+1130
</ins><span class="cx"> TZOFFSETTO:+1200
</span><ins>+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19290317T020000
+RRULE:FREQ=YEARLY;UNTIL=19330318T140000Z;BYDAY=3SU;BYMONTH=3
+TZNAME:NZMT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1130
</ins><span class="cx"> END:STANDARD
</span><ins>+BEGIN:STANDARD
+DTSTART:19340429T020000
+RRULE:FREQ=YEARLY;UNTIL=19400427T140000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:NZMT
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1130
+END:STANDARD
</ins><span class="cx"> BEGIN:DAYLIGHT
</span><ins>+DTSTART:19340930T020000
+RRULE:FREQ=YEARLY;UNTIL=19400928T143000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:NZST
+TZOFFSETFROM:+1130
+TZOFFSETTO:+1200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19460101T000000
+RDATE:19460101T000000
+TZNAME:NZST
+TZOFFSETFROM:+1200
+TZOFFSETTO:+1200
+END:STANDARD
+BEGIN:DAYLIGHT
</ins><span class="cx"> DTSTART:19741103T020000
</span><span class="cx"> RDATE:19741103T020000
</span><span class="cx"> RDATE:19891008T020000
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaAmmanics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Amman.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Amman.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Amman.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -106,7 +106,7 @@
</span><span class="cx"> END:DAYLIGHT
</span><span class="cx"> BEGIN:DAYLIGHT
</span><span class="cx"> DTSTART:20020328T235959
</span><del>-RRULE:FREQ=YEARLY;BYDAY=-1TH;BYMONTH=3
</del><ins>+RRULE:FREQ=YEARLY;UNTIL=20120329T215959Z;BYDAY=-1TH;BYMONTH=3
</ins><span class="cx"> TZNAME:EEST
</span><span class="cx"> TZOFFSETFROM:+0200
</span><span class="cx"> TZOFFSETTO:+0300
</span><span class="lines">@@ -118,26 +118,12 @@
</span><span class="cx"> TZOFFSETFROM:+0300
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20130328T235959
-RDATE:20130328T235959
-TZNAME:EEST
-TZOFFSETFROM:+0300
-TZOFFSETTO:+0300
-END:DAYLIGHT
</del><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:20131025T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:EET
</del><ins>+DTSTART:20121026T010000
+RDATE:20121026T010000
+TZNAME:AST
</ins><span class="cx"> TZOFFSETFROM:+0300
</span><del>-TZOFFSETTO:+0200
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20140327T235959
-RRULE:FREQ=YEARLY;BYDAY=-1TH;BYMONTH=3
-TZNAME:EEST
-TZOFFSETFROM:+0200
</del><span class="cx"> TZOFFSETTO:+0300
</span><del>-END:DAYLIGHT
</del><ins>+END:STANDARD
</ins><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaDiliics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Dili.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Dili.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Dili.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19760503T000000
</span><span class="cx"> RDATE:19760503T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+0900
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaGazaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Gaza.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Gaza.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Gaza.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -43,6 +43,7 @@
</span><span class="cx"> RDATE:20090904T010000
</span><span class="cx"> RDATE:20100811T000000
</span><span class="cx"> RDATE:20110801T000000
</span><ins>+RDATE:20120921T010000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0300
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -186,7 +187,7 @@
</span><span class="cx"> TZOFFSETTO:+0300
</span><span class="cx"> END:DAYLIGHT
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:20120921T010000
</del><ins>+DTSTART:20130927T000000
</ins><span class="cx"> RRULE:FREQ=YEARLY;BYDAY=FR;BYMONTHDAY=21,22,23,24,25,26,27;BYMONTH=9
</span><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0300
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaHebronics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Hebron.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Hebron.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Hebron.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx"> RDATE:20100811T000000
</span><span class="cx"> RDATE:20110801T000000
</span><span class="cx"> RDATE:20110930T000000
</span><ins>+RDATE:20120921T010000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0300
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -178,7 +179,7 @@
</span><span class="cx"> TZOFFSETTO:+0300
</span><span class="cx"> END:DAYLIGHT
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:20120921T010000
</del><ins>+DTSTART:20130927T000000
</ins><span class="cx"> RRULE:FREQ=YEARLY;BYDAY=FR;BYMONTHDAY=21,22,23,24,25,26,27;BYMONTH=9
</span><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0300
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaJakartaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jakarta.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jakarta.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jakarta.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -8,7 +8,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:18670810T000000
</span><span class="cx"> RDATE:18670810T000000
</span><del>-TZNAME:JMT
</del><ins>+TZNAME:BMT
</ins><span class="cx"> TZOFFSETFROM:+070712
</span><span class="cx"> TZOFFSETTO:+070712
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19321101T000000
</span><span class="cx"> RDATE:19321101T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0720
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -36,28 +36,28 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19450923T000000
</span><span class="cx"> RDATE:19450923T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0900
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19480501T000000
</span><span class="cx"> RDATE:19480501T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0730
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19500501T000000
</span><span class="cx"> RDATE:19500501T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0800
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19640101T000000
</span><span class="cx"> RDATE:19640101T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0730
</span><span class="cx"> TZOFFSETTO:+0700
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaJayapuraics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jayapura.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jayapura.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Jayapura.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -8,7 +8,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19321101T000000
</span><span class="cx"> RDATE:19321101T000000
</span><del>-TZNAME:EIT
</del><ins>+TZNAME:WIT
</ins><span class="cx"> TZOFFSETFROM:+092248
</span><span class="cx"> TZOFFSETTO:+0900
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -22,7 +22,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19640101T000000
</span><span class="cx"> RDATE:19640101T000000
</span><del>-TZNAME:EIT
</del><ins>+TZNAME:WIT
</ins><span class="cx"> TZOFFSETFROM:+0930
</span><span class="cx"> TZOFFSETTO:+0900
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaMakassarics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Makassar.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Makassar.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Makassar.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19321101T000000
</span><span class="cx"> RDATE:19321101T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+075736
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19450923T000000
</span><span class="cx"> RDATE:19450923T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+0900
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaPontianakics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Pontianak.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Pontianak.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Pontianak.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19321101T000000
</span><span class="cx"> RDATE:19321101T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+071720
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -29,35 +29,35 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19450923T000000
</span><span class="cx"> RDATE:19450923T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0900
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19480501T000000
</span><span class="cx"> RDATE:19480501T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0730
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19500501T000000
</span><span class="cx"> RDATE:19500501T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0800
</span><span class="cx"> TZOFFSETTO:+0730
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19640101T000000
</span><span class="cx"> RDATE:19640101T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+0730
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19880101T000000
</span><span class="cx"> RDATE:19880101T000000
</span><del>-TZNAME:WIT
</del><ins>+TZNAME:WIB
</ins><span class="cx"> TZOFFSETFROM:+0800
</span><span class="cx"> TZOFFSETTO:+0700
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoAsiaUjung_Pandangics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Ujung_Pandang.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Ujung_Pandang.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Asia/Ujung_Pandang.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19321101T000000
</span><span class="cx"> RDATE:19321101T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+075736
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19450923T000000
</span><span class="cx"> RDATE:19450923T000000
</span><del>-TZNAME:CIT
</del><ins>+TZNAME:WITA
</ins><span class="cx"> TZOFFSETFROM:+0900
</span><span class="cx"> TZOFFSETTO:+0800
</span><span class="cx"> END:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeBusingenics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Busingen.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Busingen.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Busingen.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,17 +6,17 @@
</span><span class="cx"> TZID:Europe/Busingen
</span><span class="cx"> X-LIC-LOCATION:Europe/Busingen
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:18480912T000000
-RDATE:18480912T000000
</del><ins>+DTSTART:18530716T000000
+RDATE:18530716T000000
</ins><span class="cx"> TZNAME:BMT
</span><span class="cx"> TZOFFSETFROM:+003408
</span><del>-TZOFFSETTO:+002944
</del><ins>+TZOFFSETTO:+002946
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:18940601T000000
</span><span class="cx"> RDATE:18940601T000000
</span><span class="cx"> TZNAME:CEST
</span><del>-TZOFFSETFROM:+002944
</del><ins>+TZOFFSETFROM:+002946
</ins><span class="cx"> TZOFFSETTO:+0100
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeVaduzics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Vaduz.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Vaduz.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Vaduz.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,31 @@
</span><span class="cx"> TZID:Europe/Vaduz
</span><span class="cx"> X-LIC-LOCATION:Europe/Vaduz
</span><span class="cx"> BEGIN:STANDARD
</span><ins>+DTSTART:18530716T000000
+RDATE:18530716T000000
+TZNAME:BMT
+TZOFFSETFROM:+003408
+TZOFFSETTO:+002946
+END:STANDARD
+BEGIN:STANDARD
</ins><span class="cx"> DTSTART:18940601T000000
</span><span class="cx"> RDATE:18940601T000000
</span><ins>+TZNAME:CEST
+TZOFFSETFROM:+002946
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19410505T010000
+RRULE:FREQ=YEARLY;UNTIL=19420504T000000Z;BYDAY=1MO;BYMONTH=5
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19411006T020000
+RRULE:FREQ=YEARLY;UNTIL=19421005T000000Z;BYDAY=1MO;BYMONTH=10
</ins><span class="cx"> TZNAME:CET
</span><del>-TZOFFSETFROM:+003804
</del><ins>+TZOFFSETFROM:+0200
</ins><span class="cx"> TZOFFSETTO:+0100
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoEuropeZurichics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Zurich.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Zurich.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Europe/Zurich.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,17 +6,17 @@
</span><span class="cx"> TZID:Europe/Zurich
</span><span class="cx"> X-LIC-LOCATION:Europe/Zurich
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:18480912T000000
-RDATE:18480912T000000
</del><ins>+DTSTART:18530716T000000
+RDATE:18530716T000000
</ins><span class="cx"> TZNAME:BMT
</span><span class="cx"> TZOFFSETFROM:+003408
</span><del>-TZOFFSETTO:+002944
</del><ins>+TZOFFSETTO:+002946
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:18940601T000000
</span><span class="cx"> RDATE:18940601T000000
</span><span class="cx"> TZNAME:CEST
</span><del>-TZOFFSETFROM:+002944
</del><ins>+TZOFFSETFROM:+002946
</ins><span class="cx"> TZOFFSETTO:+0100
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoJamaicaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Jamaica.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Jamaica.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Jamaica.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -9,14 +9,14 @@
</span><span class="cx"> DTSTART:18900101T000000
</span><span class="cx"> RDATE:18900101T000000
</span><span class="cx"> TZNAME:KMT
</span><del>-TZOFFSETFROM:-050712
-TZOFFSETTO:-050712
</del><ins>+TZOFFSETFROM:-050711
+TZOFFSETTO:-050711
</ins><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19120201T000000
</span><span class="cx"> RDATE:19120201T000000
</span><span class="cx"> TZNAME:EST
</span><del>-TZOFFSETFROM:-050712
</del><ins>+TZOFFSETFROM:-050711
</ins><span class="cx"> TZOFFSETTO:-0500
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:STANDARD
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoPacificFijiics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Fiji.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Fiji.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Fiji.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -43,7 +43,7 @@
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span><span class="cx"> DTSTART:20101024T020000
</span><del>-RRULE:FREQ=YEARLY;BYDAY=-2SU;BYMONTH=10
</del><ins>+RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTHDAY=21,22,23,24,25,26,27;BYMONTH=10
</ins><span class="cx"> TZNAME:FJST
</span><span class="cx"> TZOFFSETFROM:+1200
</span><span class="cx"> TZOFFSETTO:+1300
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoPacificJohnstonics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Johnston.ics (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Johnston.ics        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/Pacific/Johnston.ics        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -6,10 +6,33 @@
</span><span class="cx"> TZID:Pacific/Johnston
</span><span class="cx"> X-LIC-LOCATION:Pacific/Johnston
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:18000101T000000
-RDATE:18000101T000000
</del><ins>+DTSTART:18960113T120000
+RDATE:18960113T120000
</ins><span class="cx"> TZNAME:HST
</span><del>-TZOFFSETFROM:-1000
</del><ins>+TZOFFSETFROM:-103126
+TZOFFSETTO:-1030
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19330430T020000
+RDATE:19330430T020000
+RDATE:19420209T020000
+TZNAME:HDT
+TZOFFSETFROM:-1030
+TZOFFSETTO:-0930
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19330521T120000
+RDATE:19330521T120000
+RDATE:19450930T020000
+TZNAME:HST
+TZOFFSETFROM:-0930
+TZOFFSETTO:-1030
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19470608T020000
+RDATE:19470608T020000
+TZNAME:HST
+TZOFFSETFROM:-1030
</ins><span class="cx"> TZOFFSETTO:-1000
</span><span class="cx"> END:STANDARD
</span><span class="cx"> END:VTIMEZONE
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfolinkstxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/links.txt (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/links.txt        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/links.txt        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-America/Virgin        America/St_Thomas
</del><ins>+America/Virgin        America/Port_of_Spain
</ins><span class="cx"> America/Buenos_Aires        America/Argentina/Buenos_Aires
</span><span class="cx"> Hongkong        Asia/Hong_Kong
</span><span class="cx"> Etc/GMT+0        Etc/GMT
</span><span class="lines">@@ -6,25 +6,28 @@
</span><span class="cx"> Australia/South        Australia/Adelaide
</span><span class="cx"> America/Atka        America/Adak
</span><span class="cx"> America/Coral_Harbour        America/Atikokan
</span><del>-Africa/Asmera        Africa/Asmara
-America/Fort_Wayne        America/Indiana/Indianapolis
-Australia/LHI        Australia/Lord_Howe
</del><ins>+America/St_Lucia        America/Port_of_Spain
+Canada/Newfoundland        America/St_Johns
+America/Montserrat        America/Port_of_Spain
</ins><span class="cx"> PRC        Asia/Shanghai
</span><span class="cx"> US/Mountain        America/Denver
</span><span class="cx"> Asia/Thimbu        Asia/Thimphu
</span><span class="cx"> America/Shiprock        America/Denver
</span><ins>+America/Grenada        America/Port_of_Spain
</ins><span class="cx"> Europe/Podgorica        Europe/Belgrade
</span><ins>+Africa/Juba        Africa/Khartoum
</ins><span class="cx"> Brazil/DeNoronha        America/Noronha
</span><span class="cx"> Jamaica        America/Jamaica
</span><span class="cx"> Arctic/Longyearbyen        Europe/Oslo
</span><span class="cx"> Europe/Guernsey        Europe/London
</span><span class="cx"> GB        Europe/London
</span><del>-Canada/Mountain        America/Edmonton
</del><ins>+America/Aruba        America/Curacao
</ins><span class="cx"> Chile/EasterIsland        Pacific/Easter
</span><span class="cx"> Etc/Universal        Etc/UTC
</span><span class="cx"> Navajo        America/Denver
</span><span class="cx"> America/Indianapolis        America/Indiana/Indianapolis
</span><span class="cx"> Pacific/Truk        Pacific/Chuuk
</span><ins>+Canada/Mountain        America/Edmonton
</ins><span class="cx"> Pacific/Yap        Pacific/Chuuk
</span><span class="cx"> America/Ensenada        America/Tijuana
</span><span class="cx"> Europe/Sarajevo        Europe/Belgrade
</span><span class="lines">@@ -46,19 +49,25 @@
</span><span class="cx"> Asia/Saigon        Asia/Ho_Chi_Minh
</span><span class="cx"> ROC        Asia/Taipei
</span><span class="cx"> America/Louisville        America/Kentucky/Louisville
</span><del>-America/St_Barthelemy        America/Guadeloupe
</del><ins>+America/St_Barthelemy        America/Port_of_Spain
+America/St_Thomas        America/Port_of_Spain
</ins><span class="cx"> America/Porto_Acre        America/Rio_Branco
</span><del>-Europe/Isle_of_Man        Europe/London
</del><ins>+America/Rosario        America/Argentina/Cordoba
+America/Guadeloupe        America/Port_of_Spain
</ins><span class="cx"> Australia/West        Australia/Perth
</span><span class="cx"> US/Eastern        America/New_York
</span><span class="cx"> Libya        Africa/Tripoli
</span><ins>+America/Fort_Wayne        America/Indiana/Indianapolis
+Antarctica/McMurdo        Pacific/Auckland
</ins><span class="cx"> Canada/Saskatchewan        America/Regina
</span><ins>+Canada/Pacific        America/Vancouver
</ins><span class="cx"> Canada/Eastern        America/Toronto
</span><span class="cx"> Iran        Asia/Tehran
</span><span class="cx"> GB-Eire        Europe/London
</span><span class="cx"> Etc/Greenwich        Etc/GMT
</span><span class="cx"> Atlantic/Jan_Mayen        Europe/Oslo
</span><span class="cx"> US/Central        America/Chicago
</span><ins>+America/St_Vincent        America/Port_of_Spain
</ins><span class="cx"> US/Pacific        America/Los_Angeles
</span><span class="cx"> Portugal        Europe/Lisbon
</span><span class="cx"> Europe/Tiraspol        Europe/Chisinau
</span><span class="lines">@@ -70,7 +79,7 @@
</span><span class="cx"> Asia/Ulan_Bator        Asia/Ulaanbaatar
</span><span class="cx"> Kwajalein        Pacific/Kwajalein
</span><span class="cx"> Australia/Yancowinna        Australia/Broken_Hill
</span><del>-America/Marigot        America/Guadeloupe
</del><ins>+America/Marigot        America/Port_of_Spain
</ins><span class="cx"> America/Lower_Princes        America/Curacao
</span><span class="cx"> Greenwich        Etc/GMT
</span><span class="cx"> America/Mendoza        America/Argentina/Mendoza
</span><span class="lines">@@ -82,7 +91,7 @@
</span><span class="cx"> Asia/Tel_Aviv        Asia/Jerusalem
</span><span class="cx"> Mexico/General        America/Mexico_City
</span><span class="cx"> Asia/Istanbul        Europe/Istanbul
</span><del>-America/Rosario        America/Argentina/Cordoba
</del><ins>+Europe/Isle_of_Man        Europe/London
</ins><span class="cx"> GMT0        Etc/GMT
</span><span class="cx"> Europe/Mariehamn        Europe/Helsinki
</span><span class="cx"> Australia/Victoria        Australia/Melbourne
</span><span class="lines">@@ -96,27 +105,33 @@
</span><span class="cx"> Asia/Ashkhabad        Asia/Ashgabat
</span><span class="cx"> America/Knox_IN        America/Indiana/Knox
</span><span class="cx"> America/Catamarca        America/Argentina/Catamarca
</span><ins>+Zulu        Etc/UTC
</ins><span class="cx"> GMT+0        Etc/GMT
</span><span class="cx"> Poland        Europe/Warsaw
</span><span class="cx"> Pacific/Samoa        Pacific/Pago_Pago
</span><span class="cx"> US/Indiana-Starke        America/Indiana/Knox
</span><del>-Canada/Newfoundland        America/St_Johns
</del><ins>+Australia/LHI        Australia/Lord_Howe
+Pacific/Johnston        Pacific/Honolulu
</ins><span class="cx"> GMT        Etc/GMT
</span><span class="cx"> Canada/Yukon        America/Whitehorse
</span><span class="cx"> Canada/Atlantic        America/Halifax
</span><span class="cx"> US/Arizona        America/Phoenix
</span><span class="cx"> Europe/San_Marino        Europe/Rome
</span><span class="cx"> Australia/NSW        Australia/Sydney
</span><del>-Canada/Pacific        America/Vancouver
</del><ins>+America/St_Kitts        America/Port_of_Spain
+Brazil/East        America/Sao_Paulo
</ins><span class="cx"> Etc/Zulu        Etc/UTC
</span><ins>+Singapore        Asia/Singapore
</ins><span class="cx"> Europe/Ljubljana        Europe/Belgrade
</span><span class="cx"> US/Alaska        America/Anchorage
</span><span class="cx"> Atlantic/Faeroe        Atlantic/Faroe
</span><span class="cx"> Etc/GMT-0        Etc/GMT
</span><ins>+America/Anguilla        America/Port_of_Spain
</ins><span class="cx"> Israel        Asia/Jerusalem
</span><span class="cx"> UCT        Etc/UCT
</span><span class="cx"> NZ-CHAT        Pacific/Chatham
</span><span class="cx"> Iceland        Atlantic/Reykjavik
</span><ins>+Brazil/Acre        America/Rio_Branco
</ins><span class="cx"> Europe/Vatican        Europe/Rome
</span><span class="cx"> Australia/Queensland        Australia/Brisbane
</span><span class="cx"> Africa/Timbuktu        Africa/Bamako
</span><span class="lines">@@ -131,9 +146,9 @@
</span><span class="cx"> Canada/Central        America/Winnipeg
</span><span class="cx"> GMT-0        Etc/GMT
</span><span class="cx"> W-SU        Europe/Moscow
</span><del>-Zulu        Etc/UTC
</del><ins>+America/Dominica        America/Port_of_Spain
</ins><span class="cx"> Egypt        Africa/Cairo
</span><del>-Singapore        Asia/Singapore
-Brazil/Acre        America/Rio_Branco
-Brazil/East        America/Sao_Paulo
-Antarctica/South_Pole        Antarctica/McMurdo
</del><span class="cx">\ No newline at end of file
</span><ins>+America/Tortola        America/Port_of_Spain
+Europe/Vaduz        Europe/Zurich
+Africa/Asmera        Africa/Asmara
+Antarctica/South_Pole        Pacific/Auckland
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfotimezonesxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/timezones.xml (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/timezones.xml        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/timezones.xml        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -2,7 +2,7 @@
</span><span class="cx"> <!DOCTYPE timezones SYSTEM "timezones.dtd">
</span><span class="cx">
</span><span class="cx"> <timezones>
</span><del>- <dtstamp>2013-07-11T02:11:45Z</dtstamp>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</ins><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Abidjan</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><span class="lines">@@ -138,8 +138,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Juba</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>2cecec633d0950df56d2022393afdfdb</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>3f633cfde1a12e6f297ba54460659a71</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Kampala</tzid>
</span><span class="lines">@@ -149,6 +149,7 @@
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Khartoum</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><ins>+ <alias>Africa/Juba</alias>
</ins><span class="cx"> <md5>e4a944da17c50b3e031e19dee17bec58</md5>
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="lines">@@ -292,8 +293,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Anguilla</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>3a0d92a114885c5ee40e6b4115e7d144</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>dbe16a1225d25666094e89067392e9c8</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Antigua</tzid>
</span><span class="lines">@@ -302,8 +303,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Araguaina</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
- <md5>2cac2a50050e86a3dcf0ce0c3aadcafd</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>4d0786c2a5a830c11420baa3adb032df</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Argentina/Buenos_Aires</tzid>
</span><span class="lines">@@ -364,8 +365,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Argentina/San_Luis</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>783baf3a55ec90ab162cb47c3fd07121</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>31db41adcfc7e217968729395ff3e670</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Argentina/Tucuman</tzid>
</span><span class="lines">@@ -379,8 +380,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Aruba</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>473119154a575c5de70495c9082565f2</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>877fdd70d2d3bfc3043c0a12ff8030af</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Asuncion</tzid>
</span><span class="lines">@@ -480,8 +481,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Cayman</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>07ca09e17378e117aac517b98ef07824</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>aed22af0be0d3c839b3ac941a21711de</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Chicago</tzid>
</span><span class="lines">@@ -524,6 +525,7 @@
</span><span class="cx"> <dtstamp>2013-05-08T18:04:04Z</dtstamp>
</span><span class="cx"> <alias>America/Kralendijk</alias>
</span><span class="cx"> <alias>America/Lower_Princes</alias>
</span><ins>+ <alias>America/Aruba</alias>
</ins><span class="cx"> <md5>0b270fa38a9e55a4c48facbf5be02f99</md5>
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="lines">@@ -557,8 +559,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Dominica</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>86c1ba04b479911b0cf0aa917a76e3fd</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>7a07f99ab572aeac2baa3466c4ac60c5</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Edmonton</tzid>
</span><span class="lines">@@ -608,20 +610,18 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Grand_Turk</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>794fd7b29a023a5722b25b99bbb6281d</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>494b352a3fb06a2b4a4dd169aa3b98db</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Grenada</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>32c4916ced899420efcc39a4ca47936e</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>ed3d7b7bb03baf025941c7939ea85ece</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Guadeloupe</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <alias>America/St_Barthelemy</alias>
- <alias>America/Marigot</alias>
- <md5>4b93fee3397a9dfc3687da25df948494</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>7b37cd74d65c5961c765350b1f492663</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Guatemala</tzid>
</span><span class="lines">@@ -717,9 +717,9 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Jamaica</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</ins><span class="cx"> <alias>Jamaica</alias>
</span><del>- <md5>d724fa4276cb5420ecc60d5371e4ceef</md5>
</del><ins>+ <md5>b7185b6351db3d2c351f83b1166c490d</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Jujuy</tzid>
</span><span class="lines">@@ -796,8 +796,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Marigot</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>5112b932cc80557d4e01190ab86f19de</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>219cf3ff91c93b07dc71298421f9d0de</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Martinique</tzid>
</span><span class="lines">@@ -868,8 +868,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Montserrat</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>1278c06be965a9444decd86efc81338d</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>33c697bb4f58afd1a902018247cd21e4</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Nassau</tzid>
</span><span class="lines">@@ -947,6 +947,19 @@
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Port_of_Spain</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><ins>+ <alias>America/Virgin</alias>
+ <alias>America/St_Lucia</alias>
+ <alias>America/Montserrat</alias>
+ <alias>America/Grenada</alias>
+ <alias>America/St_Barthelemy</alias>
+ <alias>America/St_Thomas</alias>
+ <alias>America/Guadeloupe</alias>
+ <alias>America/St_Vincent</alias>
+ <alias>America/Marigot</alias>
+ <alias>America/St_Kitts</alias>
+ <alias>America/Anguilla</alias>
+ <alias>America/Dominica</alias>
+ <alias>America/Tortola</alias>
</ins><span class="cx"> <md5>e0bb07b4ce7859ca493cb6bba549e114</md5>
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="lines">@@ -1047,8 +1060,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Barthelemy</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>0df0f96dd6aee2faae600ea4bda5792f</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>282f73e528b10401ba322ab01a1c7bd3</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Johns</tzid>
</span><span class="lines">@@ -1058,24 +1071,23 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Kitts</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>9b1065952186f4159a5aafe130eef8e2</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>40a657ac17ce9e12105d6895084ed655</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Lucia</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>7cc48ba354a2f44b1a516c388ea6ac6f</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>76cf7c0ae9c69e499de421ecb41ada4b</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Thomas</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <alias>America/Virgin</alias>
- <md5>f35dd65d25337d2b67195a4000765881</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>0dac89af79b0fa3b1d67d7a6a63aaa11</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/St_Vincent</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>e34a65b69696732682902a6bba3abb29</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>6aae72797c8fea31921bfa1b996b1442</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Swift_Current</tzid>
</span><span class="lines">@@ -1112,8 +1124,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Tortola</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>36252a7ac5c1544d56691117fe4bedf0</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>c19dd4b8748b9ffeb5aa0cc21718d26e</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Vancouver</tzid>
</span><span class="lines">@@ -1123,8 +1135,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Virgin</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>302f38a85c5ed04952bed5372587578e</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>7f6b5b25ece02b385733e3a4a49f7167</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Whitehorse</tzid>
</span><span class="lines">@@ -1175,9 +1187,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Antarctica/McMurdo</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <alias>Antarctica/South_Pole</alias>
- <md5>7866bc7215b5160ba92b9c0ff17f2567</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>3e1599b00f2814dec105fff3868e2232</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Antarctica/Palmer</tzid>
</span><span class="lines">@@ -1191,8 +1202,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Antarctica/South_Pole</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>ecbf324f6216e2aba53f2d333c26141e</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>01586fbc05c637aed3ec1f6cf889b872</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Antarctica/Syowa</tzid>
</span><span class="lines">@@ -1221,8 +1232,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Amman</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
- <md5>3d5145f59e99e4245ccca5484b38b271</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>00094f838d542836f35b1d3d0293512c</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Anadyr</tzid>
</span><span class="lines">@@ -1329,8 +1340,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Dili</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>51ad0f3231ff8a47222ed92137ea4dc3</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>f846195e2b9f145c2a35abda88302238</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Dubai</tzid>
</span><span class="lines">@@ -1344,8 +1355,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Gaza</tzid>
</span><del>- <dtstamp>2013-05-08T18:04:04Z</dtstamp>
- <md5>17173f5c545937b19c7dba20cc4c7b97</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>656f56b232fb5ad6fb2e25a64086a44c</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Harbin</tzid>
</span><span class="lines">@@ -1354,8 +1365,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Hebron</tzid>
</span><del>- <dtstamp>2013-05-08T18:04:04Z</dtstamp>
- <md5>1909080f7bc3c9c602627b4123dd13a9</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>1198057afbbaf92ca0f34b8c16416d74</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Ho_Chi_Minh</tzid>
</span><span class="lines">@@ -1386,13 +1397,13 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Jakarta</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>361f6e5683f19c99e1f024b3b80227be</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>37eb197c796a861a7817f06380623146</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Jayapura</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>292c823058149d8c8bee5398924bf64a</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>8fcec2bd8414e2cc845c807af45d1dce</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Jerusalem</tzid>
</span><span class="lines">@@ -1481,9 +1492,9 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Makassar</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</ins><span class="cx"> <alias>Asia/Ujung_Pandang</alias>
</span><del>- <md5>efbc6213ee5099feeafaeacd6bbbb797</md5>
</del><ins>+ <md5>d34ae21548d56ea2b62eb890559d46f0</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Manila</tzid>
</span><span class="lines">@@ -1528,8 +1539,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Pontianak</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>5558eaba9bfdf39ef008593707cadcda</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>257fd7f7bf01752d97f04d4deaff03be</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Pyongyang</tzid>
</span><span class="lines">@@ -1635,8 +1646,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Ujung_Pandang</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>d05b22df61dea5d57753440e8b5ef386</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>8a094c3a682a26dbdbb212bcc01e2a7e</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Asia/Ulaanbaatar</tzid>
</span><span class="lines">@@ -2234,8 +2245,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Europe/Busingen</tzid>
</span><del>- <dtstamp>2013-05-08T18:04:04Z</dtstamp>
- <md5>3a97a0f0c013fde482c37540d3d105eb</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>7e93edc4d979424daf4521a8e39fc4df</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Europe/Chisinau</tzid>
</span><span class="lines">@@ -2452,8 +2463,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Europe/Vaduz</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>a8a4e48e0a06cd9b54304b82614447c1</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>f751185606fd0cdcd1d2cf2a1bfd7d4b</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Europe/Vatican</tzid>
</span><span class="lines">@@ -2493,9 +2504,10 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Europe/Zurich</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</ins><span class="cx"> <alias>Europe/Busingen</alias>
</span><del>- <md5>189add82d7c3280b544ca70f5696e68c</md5>
</del><ins>+ <alias>Europe/Vaduz</alias>
+ <md5>f4cfe31d995ca98d545a03ef60ebbbee</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>GB</tzid>
</span><span class="lines">@@ -2614,8 +2626,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Jamaica</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>1f8889ee038dede3ef4868055adf897a</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>b5f083a6081a40b4525e7c8e2da9e963</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Japan</tzid>
</span><span class="lines">@@ -2696,6 +2708,8 @@
</span><span class="cx"> <tzid>Pacific/Auckland</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><span class="cx"> <alias>NZ</alias>
</span><ins>+ <alias>Antarctica/McMurdo</alias>
+ <alias>Antarctica/South_Pole</alias>
</ins><span class="cx"> <md5>31b52d15573225aff7940c24fbe45343</md5>
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="lines">@@ -2734,8 +2748,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Pacific/Fiji</tzid>
</span><del>- <dtstamp>2013-05-08T18:04:04Z</dtstamp>
- <md5>bdf37be1c81f84c63dcea56d21f02928</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>0cf1c77fa2dc0d8ea0383afddf501e17</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Pacific/Funafuti</tzid>
</span><span class="lines">@@ -2766,12 +2780,13 @@
</span><span class="cx"> <tzid>Pacific/Honolulu</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><span class="cx"> <alias>US/Hawaii</alias>
</span><ins>+ <alias>Pacific/Johnston</alias>
</ins><span class="cx"> <md5>be013195b929c48b73f0234a5226a763</md5>
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Pacific/Johnston</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>fdd50497d420099a0f7faabcc47e967e</md5>
</del><ins>+ <dtstamp>2013-10-01T01:19:11Z</dtstamp>
+ <md5>82a4fca854a65c81f3c9548471270441</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Pacific/Kiritimati</tzid>
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletwistedcaldavzoneinfoversiontxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/version.txt (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/version.txt        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/twistedcaldav/zoneinfo/version.txt        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -1 +1 @@
</span><del>-IANA Timezone Registry: 2013d
</del><span class="cx">\ No newline at end of file
</span><ins>+IANA Timezone Registry: 2013f
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoresubpostgrespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/subpostgres.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/subpostgres.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/subpostgres.py        2013-11-01 22:25:30 UTC (rev 11871)
</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>+ """
+ We started postgres; we're responsible for stopping it later.
+ Call pgCtl status to get the pid.
+ """
</ins><span class="cx"> log.warn("{cmd} exited", cmd=pgCtl)
</span><span class="cx"> self.shouldStopDatabase = True
</span><span class="cx"> d = Deferred()
</span><span class="lines">@@ -463,15 +467,34 @@
</span><span class="cx"> env=self.env, path=self.workingDir.path,
</span><span class="cx"> uid=self.uid, gid=self.gid,
</span><span class="cx"> )
</span><del>- d.addCallback(gotStatus)
</del><ins>+ return d.addCallback(gotStatus)
</ins><span class="cx">
</span><del>- def reportit(f):
- log.failure("starting postgres", f)
</del><ins>+ def couldNotStart(f):
+ """
+ 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.
+ """
+ d = Deferred()
+ statusMonitor = CapturingProcessProtocol(d, None)
+ self.reactor.spawnProcess(
+ statusMonitor, pgCtl, [pgCtl, "status"],
+ env=self.env, path=self.workingDir.path,
+ uid=self.uid, gid=self.gid,
+ )
+ return d.addCallback(gotStatus).addErrback(giveUp)
+
+ def giveUp(f):
+ """
+ We can't start postgres or connect to a running instance. Shut
+ down.
+ """
+ log.failure("Can't start or connect to postgres", f)
</ins><span class="cx"> self.deactivateDelayedShutdown()
</span><span class="cx"> self.reactor.stop()
</span><del>-
</del><ins>+
</ins><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 class="lines">@@ -549,6 +572,7 @@
</span><span class="cx"> # d.addCallback(maybeStopSubprocess)
</span><span class="cx"> # return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def hardStop(self):
</span><span class="cx"> """
</span><span class="cx"> Stop postgres quickly by sending it SIGQUIT
</span><span class="lines">@@ -556,5 +580,5 @@
</span><span class="cx"> if self._postgresPid is not None:
</span><span class="cx"> try:
</span><span class="cx"> os.kill(self._postgresPid, signal.SIGQUIT)
</span><del>- except OSError:
</del><ins>+ except OSError:
</ins><span class="cx"> pass
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoretesttest_subpostgrespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/test/test_subpostgres.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/test/test_subpostgres.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/test/test_subpostgres.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -189,5 +189,3 @@
</span><span class="cx"> cursor.execute("select * from import_test_table")
</span><span class="cx"> values = cursor.fetchall()
</span><span class="cx"> self.assertEquals(values, [["value1"], ["value2"]])
</span><del>-
-
</del></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavbasedatastoreutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/base/datastore/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -92,6 +92,12 @@
</span><span class="cx"> return "objectWithName:%s:%s" % (homeResourceID, name)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ # Home child objects by id
+
+ def keyForObjectWithResourceID(self, homeResourceID, resourceID):
+ return "objectWithName:%s:%s" % (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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/file.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/file.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/file.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -128,7 +128,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, ("inbox",)))
</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">@@ -140,14 +140,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, ("inbox",)))
- for objectResource in objectResources:
- if allow_shared or objectResource._parentCollection.owned():
- results.append(objectResource)
-
</del><ins>+ results = (yield self.objectResourcesWithUID(uid, ("inbox",)))
</ins><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/schedule.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/schedule.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/schedule.py        2013-11-01 22:25:30 UTC (rev 11871)
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimipinboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/inbound.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/inbound.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/inbound.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -49,11 +49,11 @@
</span><span class="cx"> # specifically, "Unhandled unsolicited response" nonsense.
</span><span class="cx"> #
</span><span class="cx"> class IMAPLogger(LegacyLogger):
</span><del>- def emit(self, level, message=None, **kwargs):
- if message is not None and message.startswith("Unhandled unsolicited response:"):
</del><ins>+ def msg(self, *message, **kwargs):
+ if message and message[0].startswith("Unhandled unsolicited response:"):
</ins><span class="cx"> return
</span><span class="cx">
</span><del>- super(IMAPLogger, self).emit(self, level, message, **kwargs)
</del><ins>+ super(IMAPLogger, self).msg(self, *message, **kwargs)
</ins><span class="cx">
</span><span class="cx"> imap4.log = IMAPLogger()
</span><span class="cx">
</span><span class="lines">@@ -112,6 +112,11 @@
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> self.reactor = reactor
</span><span class="cx">
</span><ins>+ # If we're using our dedicated account on our local server, we're free
+ # to delete all messages that arrive in the inbox so as to not let
+ # cruft build up
+ self.deleteAllMail = shouldDeleteAllMail(config.ServerHostName,
+ settings.Server, settings.Username)
</ins><span class="cx"> self.mailReceiver = MailReceiver(store, directory)
</span><span class="cx"> mailType = settings['Type']
</span><span class="cx"> if mailType.lower().startswith('pop'):
</span><span class="lines">@@ -127,7 +132,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def fetchMail(self):
</span><del>- return self.point.connect(self.factory(self.settings, self.mailReceiver))
</del><ins>+ return self.point.connect(self.factory(self.settings, self.mailReceiver,
+ self.deleteAllMail))
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -138,6 +144,29 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><ins>+def shouldDeleteAllMail(serverHostName, inboundServer, username):
+ """
+ Given the hostname of the calendar server, the hostname of the pop/imap
+ server, and the username we're using to access inbound mail, determine
+ whether we should delete all messages in the inbox or whether to leave
+ all unprocessed messages.
+
+ @param serverHostName: the calendar server hostname (config.ServerHostName)
+ @type serverHostName: C{str}
+ @param inboundServer: the pop/imap server hostname
+ @type inboundServer: C{str}
+ @param username: the name of the account we're using to retrieve mail
+ @type username: C{str}
+ @return: True if we should delete all messages from the inbox, False otherwise
+ @rtype: C{boolean}
+ """
+ return (
+ inboundServer in (serverHostName, "localhost") and
+ username == "com.apple.calendarserver"
+ )
+
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def scheduleNextMailPoll(store, seconds):
</span><span class="cx"> txn = store.newTransaction()
</span><span class="lines">@@ -156,8 +185,9 @@
</span><span class="cx"> NO_ORGANIZER_ADDRESS = 3
</span><span class="cx"> REPLY_FORWARDED_TO_ORGANIZER = 4
</span><span class="cx"> INJECTION_SUBMITTED = 5
</span><ins>+ INCOMPLETE_DSN = 6
+ UNKNOWN_FAILURE = 7
</ins><span class="cx">
</span><del>- # What about purge( ) and lowercase( )
</del><span class="cx"> def __init__(self, store, directory):
</span><span class="cx"> self.store = store
</span><span class="cx"> self.directory = directory
</span><span class="lines">@@ -363,7 +393,23 @@
</span><span class="cx">
</span><span class="cx"> # returns a deferred
</span><span class="cx"> def inbound(self, message):
</span><ins>+ """
+ Given the text of an incoming message, parse and process it.
+ The possible return values are:
</ins><span class="cx">
</span><ins>+ NO_TOKEN - there was no token in the To address
+ UNKNOWN_TOKEN - there was an unknown token in the To address
+ MALFORMED_TO_ADDRESS - we could not parse the To address at all
+ NO_ORGANIZER_ADDRESS - no ics attachment and no email to forward to
+ REPLY_FORWARDED_TO_ORGANIZER - no ics attachment, but reply forwarded
+ INJECTION_SUBMITTED - looks ok, was submitted as a work item
+ INCOMPLETE_DSN - not enough in the DSN to go on
+ UNKNOWN_FAILURE - any error we aren't specifically catching
+
+ @param message: The body of the email
+ @type message: C{str}
+ @return: Deferred firing with one of the above action codes
+ """
</ins><span class="cx"> try:
</span><span class="cx"> msg = email.message_from_string(message)
</span><span class="cx">
</span><span class="lines">@@ -376,7 +422,7 @@
</span><span class="cx"> # It's a DSN without enough to go on
</span><span class="cx"> log.error("Mail gateway can't process DSN %s"
</span><span class="cx"> % (msg['Message-ID'],))
</span><del>- return succeed(None)
</del><ins>+ return succeed(self.INCOMPLETE_DSN)
</ins><span class="cx">
</span><span class="cx"> log.info("Mail gateway received message %s from %s to %s" %
</span><span class="cx"> (msg['Message-ID'], msg['From'], msg['To']))
</span><span class="lines">@@ -386,7 +432,7 @@
</span><span class="cx"> except Exception, e:
</span><span class="cx"> # Don't let a failure of any kind stop us
</span><span class="cx"> log.error("Failed to process message: %s" % (e,))
</span><del>- return succeed(None)
</del><ins>+ return succeed(self.UNKNOWN_FAILURE)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -442,13 +488,22 @@
</span><span class="cx"> return defer.DeferredList(downloads).addCallback(self.cbFinished)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
</ins><span class="cx"> def cbDownloaded(self, lines, id):
</span><span class="cx"> self.log.debug("POP downloaded message %d" % (id,))
</span><del>- self.factory.handleMessage("\r\n".join(lines))
- self.log.debug("POP deleting message %d" % (id,))
- self.delete(id)
</del><ins>+ actionTaken = (yield self.factory.handleMessage("\r\n".join(lines)))
</ins><span class="cx">
</span><ins>+ if self.factory.deleteAllMail:
+ # Delete all mail we see
+ self.log.debug("POP deleting message %d" % (id,))
+ self.delete(id)
+ else:
+ # Delete only mail we've processed
+ if actionTaken == MailReceiver.INJECTION_SUBMITTED:
+ self.log.debug("POP deleting message %d" % (id,))
+ self.delete(id)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def cbFinished(self, results):
</span><span class="cx"> self.log.debug("POP finished")
</span><span class="cx"> return self.quit()
</span><span class="lines">@@ -460,8 +515,10 @@
</span><span class="cx">
</span><span class="cx"> protocol = POP3DownloadProtocol
</span><span class="cx">
</span><del>- def __init__(self, settings, mailReceiver):
</del><ins>+ def __init__(self, settings, mailReceiver, deleteAllMail):
+ self.settings = settings
</ins><span class="cx"> self.mailReceiver = mailReceiver
</span><ins>+ self.deleteAllMail = deleteAllMail
</ins><span class="cx"> self.noisy = False
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -477,7 +534,7 @@
</span><span class="cx">
</span><span class="cx"> def handleMessage(self, message):
</span><span class="cx"> self.log.debug("POP factory handle message")
</span><del>- self.log.debug(message)
</del><ins>+ # self.log.debug(message)
</ins><span class="cx"> return self.mailReceiver.inbound(message)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -498,12 +555,12 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def ebLogError(self, error):
</span><del>- self.log.error("IMAP Error: %s" % (error,))
</del><ins>+ self.log.error("IMAP Error: {err}", err=error)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def ebAuthenticateFailed(self, reason):
</span><del>- self.log.debug("IMAP authenticate failed for %s, trying login" %
- (self.factory.settings["Username"],))
</del><ins>+ self.log.debug("IMAP authenticate failed for {name}, trying login",
+ name=self.factory.settings["Username"])
</ins><span class="cx"> return self.login(self.factory.settings["Username"],
</span><span class="cx"> self.factory.settings["Password"]
</span><span class="cx"> ).addCallback(self.cbLoggedIn
</span><span class="lines">@@ -511,27 +568,34 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def ebLoginFailed(self, reason):
</span><del>- self.log.error("IMAP login failed for %s" %
- (self.factory.settings["Username"],))
</del><ins>+ self.log.error("IMAP login failed for {name}", name=self.factory.settings["Username"])
</ins><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def cbLoggedIn(self, result):
</span><del>- self.log.debug("IMAP logged in [%s]" % (self.state,))
</del><ins>+ self.log.debug("IMAP logged in")
</ins><span class="cx"> self.select("Inbox").addCallback(self.cbInboxSelected)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def cbInboxSelected(self, result):
</span><del>- self.log.debug("IMAP Inbox selected [%s]" % (self.state,))
- allMessages = imap4.MessageSet(1, None)
- self.fetchUID(allMessages, True).addCallback(self.cbGotUIDs)
</del><ins>+ self.log.debug("IMAP Inbox selected")
+ self.search(imap4.Query(unseen=True)).addCallback(self.cbGotSearch)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ def cbGotSearch(self, results):
+ if results:
+ ms = imap4.MessageSet()
+ for n in results:
+ ms.add(n)
+ self.fetchUID(ms).addCallback(self.cbGotUIDs)
+ else:
+ self.cbClosed(None)
+
+
</ins><span class="cx"> def cbGotUIDs(self, results):
</span><del>- self.log.debug("IMAP got uids [%s]" % (self.state,))
</del><span class="cx"> self.messageUIDs = [result['UID'] for result in results.values()]
</span><span class="cx"> self.messageCount = len(self.messageUIDs)
</span><del>- self.log.debug("IMAP Inbox has %d messages" % (self.messageCount,))
</del><ins>+ self.log.debug("IMAP Inbox has {count} unseen messages", count=self.messageCount)
</ins><span class="cx"> if self.messageCount:
</span><span class="cx"> self.fetchNextMessage()
</span><span class="cx"> else:
</span><span class="lines">@@ -540,7 +604,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def fetchNextMessage(self):
</span><del>- self.log.debug("IMAP in fetchnextmessage [%s]" % (self.state,))
</del><ins>+ # self.log.debug("IMAP in fetchnextmessage")
</ins><span class="cx"> if self.messageUIDs:
</span><span class="cx"> nextUID = self.messageUIDs.pop(0)
</span><span class="cx"> messageListToFetch = imap4.MessageSet(nextUID)
</span><span class="lines">@@ -556,8 +620,9 @@
</span><span class="cx"> self.expunge().addCallback(self.cbInboxSelected)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
</ins><span class="cx"> def cbGotMessage(self, results, messageList):
</span><del>- self.log.debug("IMAP in cbGotMessage [%s]" % (self.state,))
</del><ins>+ self.log.debug("IMAP in cbGotMessage")
</ins><span class="cx"> try:
</span><span class="cx"> messageData = results.values()[0]['RFC822']
</span><span class="cx"> except IndexError:
</span><span class="lines">@@ -567,44 +632,46 @@
</span><span class="cx"> self.fetchNextMessage()
</span><span class="cx"> return
</span><span class="cx">
</span><del>- d = self.factory.handleMessage(messageData)
- if isinstance(d, defer.Deferred):
- d.addCallback(self.cbFlagDeleted, messageList)
</del><ins>+ actionTaken = (yield self.factory.handleMessage(messageData))
+ if self.factory.deleteAllMail:
+ # Delete all mail we see
+ yield self.cbFlagDeleted(messageList)
</ins><span class="cx"> else:
</span><del>- # No deferred returned, so no need for addCallback( )
- self.cbFlagDeleted(None, messageList)
</del><ins>+ # Delete only mail we've processed; the rest are left flagged Seen
+ if actionTaken == MailReceiver.INJECTION_SUBMITTED:
+ yield self.cbFlagDeleted(messageList)
+ else:
+ self.fetchNextMessage()
</ins><span class="cx">
</span><span class="cx">
</span><del>- def cbFlagDeleted(self, results, messageList):
</del><ins>+ def cbFlagDeleted(self, messageList):
</ins><span class="cx"> self.addFlags(messageList, ("\\Deleted",),
</span><span class="cx"> uid=True).addCallback(self.cbMessageDeleted, messageList)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def cbMessageDeleted(self, results, messageList):
</span><del>- self.log.debug("IMAP in cbMessageDeleted [%s]" % (self.state,))
</del><span class="cx"> self.log.debug("Deleted message")
</span><span class="cx"> self.fetchNextMessage()
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def cbClosed(self, results):
</span><del>- self.log.debug("IMAP in cbClosed [%s]" % (self.state,))
</del><span class="cx"> self.log.debug("Mailbox closed")
</span><span class="cx"> self.logout().addCallback(
</span><span class="cx"> lambda _: self.transport.loseConnection())
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def rawDataReceived(self, data):
</span><del>- self.log.debug("RAW RECEIVED: %s" % (data,))
</del><ins>+ # self.log.debug("RAW RECEIVED: {data}", data=data)
</ins><span class="cx"> imap4.IMAP4Client.rawDataReceived(self, data)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def lineReceived(self, line):
</span><del>- self.log.debug("RECEIVED: %s" % (line,))
</del><ins>+ # self.log.debug("RECEIVED: {line}", line=line)
</ins><span class="cx"> imap4.IMAP4Client.lineReceived(self, line)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def sendLine(self, line):
</span><del>- self.log.debug("SENDING: %s" % (line,))
</del><ins>+ # self.log.debug("SENDING: {line}", line=line)
</ins><span class="cx"> imap4.IMAP4Client.sendLine(self, line)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -614,11 +681,12 @@
</span><span class="cx">
</span><span class="cx"> protocol = IMAP4DownloadProtocol
</span><span class="cx">
</span><del>- def __init__(self, settings, mailReceiver):
</del><ins>+ def __init__(self, settings, mailReceiver, deleteAllMail):
</ins><span class="cx"> self.log.debug("Setting up IMAPFactory")
</span><span class="cx">
</span><span class="cx"> self.settings = settings
</span><span class="cx"> self.mailReceiver = mailReceiver
</span><ins>+ self.deleteAllMail = deleteAllMail
</ins><span class="cx"> self.noisy = False
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -633,7 +701,7 @@
</span><span class="cx">
</span><span class="cx"> def handleMessage(self, message):
</span><span class="cx"> self.log.debug("IMAP factory handle message")
</span><del>- self.log.debug(message)
</del><ins>+ # self.log.debug(message)
</ins><span class="cx"> return self.mailReceiver.inbound(message)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimiptesttest_inboundpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -15,7 +15,7 @@
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx">
</span><del>-from twisted.internet.defer import inlineCallbacks
</del><ins>+from twisted.internet.defer import inlineCallbacks, succeed
</ins><span class="cx"> from twisted.python.modules import getModule
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.config import ConfigDict
</span><span class="lines">@@ -25,6 +25,8 @@
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.inbound import MailReceiver
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.inbound import MailRetriever
</span><span class="cx"> from txdav.caldav.datastore.scheduling.imip.inbound import injectMessage
</span><ins>+from txdav.caldav.datastore.scheduling.imip.inbound import shouldDeleteAllMail
+from txdav.caldav.datastore.scheduling.imip.inbound import IMAP4DownloadProtocol
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
</span><span class="cx"> from txdav.caldav.datastore.test.util import buildCalendarStore
</span><span class="cx">
</span><span class="lines">@@ -47,6 +49,7 @@
</span><span class="cx"> "UseSSL" : False,
</span><span class="cx"> "Server" : "example.com",
</span><span class="cx"> "Port" : 123,
</span><ins>+ "Username" : "xyzzy",
</ins><span class="cx"> })
</span><span class="cx"> )
</span><span class="cx">
</span><span class="lines">@@ -359,3 +362,87 @@
</span><span class="cx"> ))
</span><span class="cx"> yield txn.commit()
</span><span class="cx"> yield wp.whenExecuted()
</span><ins>+
+
+ def test_shouldDeleteAllMail(self):
+
+ # Delete if the mail server is on the same host and using our
+ # dedicated account:
+ self.assertTrue(shouldDeleteAllMail("calendar.example.com",
+ "calendar.example.com", "com.apple.calendarserver"))
+ self.assertTrue(shouldDeleteAllMail("calendar.example.com",
+ "localhost", "com.apple.calendarserver"))
+
+ # Don't delete all otherwise:
+ self.assertFalse(shouldDeleteAllMail("calendar.example.com",
+ "calendar.example.com", "not_ours"))
+ self.assertFalse(shouldDeleteAllMail("calendar.example.com",
+ "localhost", "not_ours"))
+ self.assertFalse(shouldDeleteAllMail("calendar.example.com",
+ "mail.example.com", "com.apple.calendarserver"))
+
+
+ @inlineCallbacks
+ def test_deletion(self):
+ """
+ Verify the IMAP protocol will delete messages only when the right
+ conditions are met. Either:
+
+ A) We've been told to delete all mail
+ B) We've not been told to delete all mail, but it was a message
+ we processed
+ """
+
+ def stubFetchNextMessage():
+ pass
+
+ def stubCbFlagDeleted(result):
+ self.flagDeletedResult = result
+ return succeed(None)
+
+ proto = IMAP4DownloadProtocol()
+ self.patch(proto, "fetchNextMessage", stubFetchNextMessage)
+ self.patch(proto, "cbFlagDeleted", stubCbFlagDeleted)
+ results = {
+ "ignored" : (
+ {
+ "RFC822" : "a message"
+ }
+ )
+ }
+
+ # Delete all mail = False; action taken = submitted; result = deletion
+ proto.factory = StubFactory(MailReceiver.INJECTION_SUBMITTED, False)
+ self.flagDeletedResult = None
+ yield proto.cbGotMessage(results, "xyzzy")
+ self.assertEquals(self.flagDeletedResult, "xyzzy")
+
+ # Delete all mail = False; action taken = not submitted; result = no deletion
+ proto.factory = StubFactory(MailReceiver.NO_TOKEN, False)
+ self.flagDeletedResult = None
+ yield proto.cbGotMessage(results, "xyzzy")
+ self.assertEquals(self.flagDeletedResult, None)
+
+ # Delete all mail = True; action taken = submitted; result = deletion
+ proto.factory = StubFactory(MailReceiver.INJECTION_SUBMITTED, True)
+ self.flagDeletedResult = None
+ yield proto.cbGotMessage(results, "xyzzy")
+ self.assertEquals(self.flagDeletedResult, "xyzzy")
+
+ # Delete all mail = True; action taken = not submitted; result = deletion
+ proto.factory = StubFactory(MailReceiver.NO_TOKEN, True)
+ self.flagDeletedResult = None
+ yield proto.cbGotMessage(results, "xyzzy")
+ self.assertEquals(self.flagDeletedResult, "xyzzy")
+
+
+
+class StubFactory(object):
+
+ def __init__(self, actionTaken, deleteAllMail):
+ self.actionTaken = actionTaken
+ self.deleteAllMail = deleteAllMail
+
+
+ def handleMessage(self, messageData):
+ return succeed(self.actionTaken)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingimplicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/implicit.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/implicit.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/implicit.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -56,10 +56,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">@@ -383,7 +383,7 @@
</span><span class="cx"> if self.txn.doing_attendee_refresh == 0:
</span><span class="cx"> delattr(self.txn, "doing_attendee_refresh")
</span><span class="cx">
</span><del>- if refreshCount:
</del><ins>+ if refreshCount and self.logItems is not None:
</ins><span class="cx"> self.logItems["itip.refreshes"] = refreshCount
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -925,7 +925,8 @@
</span><span class="cx"> if self.action in ("create", "modify",):
</span><span class="cx"> total += (yield self.processRequests())
</span><span class="cx">
</span><del>- self.logItems["itip.requests"] = total
</del><ins>+ if self.logItems is not None:
+ self.logItems["itip.requests"] = total
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -1304,7 +1305,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["itip.reply"] = "reply"
</del><ins>+ if self.logItems is not None:
+ self.logItems["itip.reply"] = "reply"
</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">@@ -1317,7 +1319,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["itip.reply"] = "cancel"
</del><ins>+ if self.logItems is not None:
+ self.logItems["itip.reply"] = "cancel"
</ins><span class="cx">
</span><span class="cx"> itipmsg = iTipGenerator.generateAttendeeReply(self.calendar, self.attendee, force_decline=True)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingitippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/itip.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/itip.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/itip.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -471,16 +471,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("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
-
- # Set value empty
- private_comment.setValue("")
-
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingprocessingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/processing.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/processing.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/processing.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -359,7 +359,7 @@
</span><span class="cx"> # refresh them. To prevent a race we need a lock.
</span><span class="cx"> yield NamedLock.acquire(txn, "ImplicitUIDLock:%s" % (hashlib.md5(self.uid).hexdigest(),))
</span><span class="cx">
</span><del>- organizer_home = (yield txn.calendarHomeForUID(self.organizer_uid))
</del><ins>+ organizer_home = (yield txn.calendarHomeWithUID(self.organizer_uid))
</ins><span class="cx"> organizer_resource = (yield organizer_home.objectResourceWithID(self.organizer_calendar_resource_id))
</span><span class="cx"> if organizer_resource is not None:
</span><span class="cx"> yield self._doRefresh(organizer_resource, only_attendees=attendeesToProcess)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingtesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/test/test_implicit.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/test/test_implicit.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/test/test_implicit.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -21,10 +21,12 @@
</span><span class="cx"> from twext.web2 import responsecode
</span><span class="cx"> from twext.web2.http import HTTPError
</span><span class="cx">
</span><ins>+from twisted.internet import reactor
</ins><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><ins>+from twisted.internet.task import deferLater
</ins><span class="cx"> from twisted.trial.unittest import TestCase
</span><ins>+
</ins><span class="cx"> from twistedcaldav.config import config
</span><del>-
</del><span class="cx"> from twistedcaldav.ical import Component
</span><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
</span><span class="lines">@@ -1412,3 +1414,91 @@
</span><span class="cx">
</span><span class="cx"> calendar3 = (yield self._getCalendarData("user03"))
</span><span class="cx"> self.assertTrue("PARTSTAT=ACCEPTED" in calendar3)
</span><ins>+
+
+ @inlineCallbacks
+ def test_doImplicitScheduling_refreshAllAttendeesExceptSome_Batched(self):
+ """
+ Test that doImplicitScheduling delivers scheduling messages to attendees who can then reply.
+ Verify that batched refreshing is working.
+ """
+
+ data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01@example.com
+ATTENDEE:mailto:user01@example.com
+ATTENDEE:mailto:user02@example.com
+ATTENDEE:mailto:user03@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+ data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-attendee-reply
+DTSTAMP:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user01@example.com
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02@example.com
+ATTENDEE:mailto:user03@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ # Need refreshes to occur immediately, not via reactor.callLater
+ self.patch(config.Scheduling.Options, "AttendeeRefreshBatch", 5)
+ self.patch(config.Scheduling.Options, "AttendeeRefreshBatchDelaySeconds", 1)
+
+ yield self._createCalendarObject(data1, "user01", "test.ics")
+
+ list1 = (yield self._listCalendarObjects("user01", "inbox"))
+ self.assertEqual(len(list1), 0)
+
+ calendar1 = (yield self._getCalendarData("user01", "test.ics"))
+ self.assertTrue("SCHEDULE-STATUS=1.2" in calendar1)
+
+ list2 = (yield self._listCalendarObjects("user02", "inbox"))
+ self.assertEqual(len(list2), 1)
+
+ calendar2 = (yield self._getCalendarData("user02"))
+ self.assertTrue("PARTSTAT=ACCEPTED" not in calendar2)
+
+ list3 = (yield self._listCalendarObjects("user03", "inbox"))
+ self.assertEqual(len(list3), 1)
+
+ calendar3 = (yield self._getCalendarData("user03"))
+ self.assertTrue("PARTSTAT=ACCEPTED" not in calendar3)
+
+ yield self._setCalendarData(data2, "user02")
+
+ list1 = (yield self._listCalendarObjects("user01", "inbox"))
+ self.assertEqual(len(list1), 1)
+
+ calendar1 = (yield self._getCalendarData("user01", "test.ics"))
+ self.assertTrue("SCHEDULE-STATUS=2.0" in calendar1)
+ self.assertTrue("PARTSTAT=ACCEPTED" in calendar1)
+
+ list2 = (yield self._listCalendarObjects("user02", "inbox"))
+ self.assertEqual(len(list2), 1)
+
+ calendar2 = (yield self._getCalendarData("user02"))
+ self.assertTrue("PARTSTAT=ACCEPTED" in calendar2)
+
+ @inlineCallbacks
+ def _test_user03_refresh():
+ list3 = (yield self._listCalendarObjects("user03", "inbox"))
+ self.assertEqual(len(list3), 1)
+
+ calendar3 = (yield self._getCalendarData("user03"))
+ self.assertTrue("PARTSTAT=ACCEPTED" in calendar3)
+
+ yield deferLater(reactor, 2.0, _test_user03_refresh)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreschedulingutilspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/utils.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/utils.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/scheduling/utils.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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">@@ -34,7 +34,7 @@
</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) > 1:
</span><span class="cx"> # Delete all but the first one
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/sql.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/sql.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/sql.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -527,9 +527,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, ["inbox"], 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">@@ -541,15 +539,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):
+ """
+ Find all calendar object resources in the calendar home that are not in the "inbox" 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, ["inbox"]))
- 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}
+ """
</ins><span class="cx">
</span><del>- returnValue(results)
</del><ins>+ if not hasattr(self, "_cachedCalendarResourcesForUID"):
+ self._cachedCalendarResourcesForUID = {}
+ if uid not in self._cachedCalendarResourcesForUID:
+ self._cachedCalendarResourcesForUID[uid] = (yield self.objectResourcesWithUID(uid, ["inbox"], allowShared=False))
+ returnValue(self._cachedCalendarResourcesForUID[uid])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -1576,10 +1581,6 @@
</span><span class="cx"> if calsize > 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">@@ -1597,6 +1598,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">@@ -1731,20 +1736,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 "X-CALENDARSERVER-ATTENDEE-COMMENT" property. We will now allow clients
+ to delete the "X-CALENDARSERVER-PRIVATE-COMMENT" 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"> """
</span><span class="cx"> if config.Scheduling.CalDAV.get("EnablePrivateComments", 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>- "X-CALENDARSERVER-PRIVATE-COMMENT",
</del><span class="cx"> "X-CALENDARSERVER-ATTENDEE-COMMENT",
</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("Private Comments properties were entirely removed by the client. Restoring existing properties.")
</del><ins>+ log.debug("Organizer private comment properties were entirely removed by the client. Restoring existing properties.")
</ins><span class="cx"> old_calendar = (yield self.componentForUser())
</span><span class="cx"> component.transferProperties(old_calendar, (
</span><del>- "X-CALENDARSERVER-PRIVATE-COMMENT",
</del><span class="cx"> "X-CALENDARSERVER-ATTENDEE-COMMENT",
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="lines">@@ -1953,7 +1961,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">@@ -2610,7 +2618,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">@@ -2929,7 +2937,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("Attendees are not allowed to manipulate managed attachments")
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/common.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/common.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/common.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -84,73 +84,75 @@
</span><span class="cx">
</span><span class="cx"> OTHER_HOME_UID = "home_splits"
</span><span class="cx">
</span><del>-test_event_text = (
- "BEGIN:VCALENDAR\r\n"
- "VERSION:2.0\r\n"
- "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
- "CALSCALE:GREGORIAN\r\n"
- "BEGIN:VTIMEZONE\r\n"
- "TZID:US/Pacific\r\n"
- "BEGIN:DAYLIGHT\r\n"
- "TZOFFSETFROM:-0800\r\n"
- "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n"
- "DTSTART:20070311T020000\r\n"
- "TZNAME:PDT\r\n"
- "TZOFFSETTO:-0700\r\n"
- "END:DAYLIGHT\r\n"
- "BEGIN:STANDARD\r\n"
- "TZOFFSETFROM:-0700\r\n"
- "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n"
- "DTSTART:20071104T020000\r\n"
- "TZNAME:PST\r\n"
- "TZOFFSETTO:-0800\r\n"
- "END:STANDARD\r\n"
- "END:VTIMEZONE\r\n"
- "BEGIN:VEVENT\r\n"
- "CREATED:20100203T013849Z\r\n"
- "UID:uid-test\r\n"
- "DTEND;TZID=US/Pacific:20100207T173000\r\n"
- "TRANSP:OPAQUE\r\n"
- "SUMMARY:New Event\r\n"
- "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
- "DTSTAMP:20100203T013909Z\r\n"
- "SEQUENCE:3\r\n"
- "X-APPLE-DROPBOX:/calendars/users/wsanchez/dropbox/uid-test.dropbox\r\n"
- "BEGIN:VALARM\r\n"
- "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
- "TRIGGER:-PT20M\r\n"
- "ATTACH:Basso\r\n"
- "ACTION:AUDIO\r\n"
- "END:VALARM\r\n"
- "END:VEVENT\r\n"
- "END:VCALENDAR\r\n"
-)
</del><ins>+test_event_text = """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
+""".replace("\n", "\r\n")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-test_event_notCalDAV_text = (
- "BEGIN:VCALENDAR\r\n"
- "VERSION:2.0\r\n"
- "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
- "CALSCALE:GREGORIAN\r\n"
- "BEGIN:VEVENT\r\n"
- "CREATED:20100203T013849Z\r\n"
- "UID:test\r\n"
- "DTEND;TZID=US/Pacific:20100207T173000\r\n" # TZID without VTIMEZONE
- "TRANSP:OPAQUE\r\n"
- "SUMMARY:New Event\r\n"
- "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
- "DTSTAMP:20100203T013909Z\r\n"
- "SEQUENCE:3\r\n"
- "BEGIN:VALARM\r\n"
- "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
- "TRIGGER:-PT20M\r\n"
- "ATTACH:Basso\r\n"
- "ACTION:AUDIO\r\n"
- "END:VALARM\r\n"
- "END:VEVENT\r\n"
- "END:VCALENDAR\r\n"
-)
</del><ins>+test_event_notCalDAV_text = """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
+""".replace("\n", "\r\n")
</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("abc", 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"> "/CalDAV/example.com/home1/notification/",
</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"> "home1"
</span><span class="lines">@@ -469,9 +470,7 @@
</span><span class="cx"> abc = yield notifications.notificationObjectWithUID("abc")
</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"> "/CalDAV/example.com/home1/notification/",
</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("/CalDAV/example.com/home1/" 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("/CalDAV/example.com/home1/" 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"> "/CalDAV/example.com/home1/calendar_1/",
</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"> "/CalDAV/example.com/home1/calendar_1/",
</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("1.ics")
</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"> "/CalDAV/example.com/home1/calendar_1/",
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_implicit.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_implicit.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_implicit.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</span><del>- Test that resource private comments are restored.
</del><ins>+ Test that attendee private comments are no longer restored.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> data1 = """BEGIN:VCALENDAR
</span><span class="lines">@@ -524,12 +524,65 @@
</span><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar1 = (yield calendar_resource.component())
</span><span class="cx"> calendar1 = str(calendar1).replace("\r\n ", "")
</span><del>- self.assertTrue("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
</del><ins>+ self.assertFalse("X-CALENDARSERVER-PRIVATE-COMMENT:My Comment" in calendar1)
</ins><span class="cx"> self.assertTrue("SUMMARY:Changed" 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):
+ """
+ Test that organizer private comments are restored.
+ """
+
+ data1 = """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="urn:uuid:user01";
+ X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment
+END:VEVENT
+END:VCALENDAR
+"""
+
+ calendar_collection = (yield self.calendarUnderTest(home="user01"))
+ calendar = Component.fromString(data1)
+ yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
+ yield self.commit()
+
+ data2 = """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
+"""
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar = Component.fromString(data2)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
+ yield calendar_resource.setComponent(calendar)
+ yield self.commit()
+
+ calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
+ calendar1 = (yield calendar_resource.component())
+ calendar1 = str(calendar1).replace("\r\n ", "")
+ self.assertTrue("X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF=\"urn:uuid:user01\";X-CALENDARSERVER-DTSTAMP=20131101T100000Z:Someone else's comment" in calendar1)
+ self.assertTrue("SUMMARY:Changed" in calendar1)
+ yield self.commit()
+
+
+ @inlineCallbacks
</ins><span class="cx"> def test_validation_replaceMissingToDoProperties_OrganizerAttendee(self):
</span><span class="cx"> """
</span><span class="cx"> Test that missing scheduling properties in VTODOs are recovered.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_sql.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_sql.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_sql.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -444,7 +444,7 @@
</span><span class="cx"> )
</span><span class="cx"> yield migrateHome(fromHome, toHome, lambda x: x.component())
</span><span class="cx"> toCalendars = yield toHome.calendars()
</span><del>- self.assertEquals(set([c.name() for c in toCalendars]),
</del><ins>+ self.assertEquals(set([c.name() for c in toCalendars if c.name() != "inbox"]),
</ins><span class="cx"> set([k for k in self.requirements['home1'].keys()
</span><span class="cx"> if self.requirements['home1'][k] is not None]))
</span><span class="cx"> fromCalendars = yield fromHome.calendars()
</span><span class="lines">@@ -474,7 +474,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> supported_components = set()
</span><del>- self.assertEqual(len(toCalendars), 3)
</del><ins>+ self.assertEqual(len(toCalendars), 4)
</ins><span class="cx"> for calendar in toCalendars:
</span><span class="cx"> if calendar.name() == "inbox":
</span><span class="cx"> continue
</span><span class="lines">@@ -502,7 +502,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> supported_components = set()
</span><del>- self.assertEqual(len(toCalendars), 2)
</del><ins>+ self.assertEqual(len(toCalendars), 3)
</ins><span class="cx"> for calendar in toCalendars:
</span><span class="cx"> if calendar.name() == "inbox":
</span><span class="cx"> continue
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoretesttest_utilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/test/test_util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -354,20 +354,19 @@
</span><span class="cx"> }, self.storeUnderTest())
</span><span class="cx"> txn = self.transactionUnderTest()
</span><span class="cx"> emptyHome = yield txn.calendarHomeWithUID("empty_home")
</span><del>- self.assertIdentical((yield emptyHome.calendarWithName("calendar")),
- None)
</del><ins>+ self.assertIdentical((yield emptyHome.calendarWithName("calendar")), None)
</ins><span class="cx"> nonEmpty = yield txn.calendarHomeWithUID("non_empty_home")
</span><span class="cx"> yield migrateHome(emptyHome, nonEmpty)
</span><span class="cx"> yield self.commit()
</span><span class="cx"> txn = self.transactionUnderTest()
</span><span class="cx"> emptyHome = yield txn.calendarHomeWithUID("empty_home")
</span><span class="cx"> nonEmpty = yield txn.calendarHomeWithUID("non_empty_home")
</span><del>- self.assertIdentical((yield nonEmpty.calendarWithName("inbox")),
- None)
- self.assertIdentical((yield nonEmpty.calendarWithName("calendar")),
- None)
</del><span class="cx">
</span><ins>+ self.assertIdentical((yield nonEmpty.calendarWithName("calendar")), None)
+ self.assertNotIdentical((yield nonEmpty.calendarWithName("inbox")), None)
+ self.assertNotIdentical((yield nonEmpty.calendarWithName("other-default-calendar")), None)
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> @staticmethod
</span><span class="cx"> def sampleEvent(uid, summary=None):
</span><span class="cx"> """
</span><span class="lines">@@ -526,16 +525,25 @@
</span><span class="cx"> "different-name": self.sampleEvent("other-uid", "tgt other"),
</span><span class="cx"> },
</span><span class="cx"> )
</span><ins>+
</ins><span class="cx"> txn = self.transactionUnderTest()
</span><del>- c1 = yield txn.calendarHomeWithUID("conflict1")
</del><span class="cx"> c2 = yield txn.calendarHomeWithUID("conflict2")
</span><span class="cx"> otherCal = yield c2.createCalendarWithName("othercal")
</span><del>- otherCal.createCalendarObjectWithName(
</del><ins>+ yield otherCal.createCalendarObjectWithName(
</ins><span class="cx"> "some-name", Component.fromString(
</span><span class="cx"> self.sampleEvent("oc", "target calendar")[0]
</span><span class="cx"> )
</span><span class="cx"> )
</span><ins>+ yield self.commit()
+
+ txn = self.transactionUnderTest()
+ c1 = yield txn.calendarHomeWithUID("conflict1")
+ c2 = yield txn.calendarHomeWithUID("conflict2")
</ins><span class="cx"> yield migrateHome(c1, c2, merge=True)
</span><ins>+ yield self.commit()
+
+ txn = self.transactionUnderTest()
+ c2 = yield txn.calendarHomeWithUID("conflict2")
</ins><span class="cx"> targetCal = yield c2.calendarWithName("conflicted")
</span><span class="cx"> yield self.checkSummary("same-name", "target", targetCal)
</span><span class="cx"> yield self.checkSummary("different-name", "tgt other", targetCal)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcaldavdatastoreutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/caldav/datastore/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -356,8 +356,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>-def migrateHome(inHome, outHome, getComponent=lambda x: x.component(),
- merge=False):
</del><ins>+def migrateHome(inHome, outHome, getComponent=lambda x: x.component(), merge=False):
</ins><span class="cx"> """
</span><span class="cx"> Copy all calendars and properties in the given input calendar home to the
</span><span class="cx"> given output calendar home.
</span><span class="lines">@@ -373,7 +372,7 @@
</span><span class="cx"> a calendar in outHome).
</span><span class="cx">
</span><span class="cx"> @param merge: a boolean indicating whether to raise an exception when
</span><del>- encounting a conflicting element of data (calendar or event), or to
</del><ins>+ encountering a conflicting element of data (calendar or event), or to
</ins><span class="cx"> attempt to merge them together.
</span><span class="cx">
</span><span class="cx"> @return: a L{Deferred} that fires with C{None} when the migration is
</span><span class="lines">@@ -398,8 +397,7 @@
</span><span class="cx"> yield d
</span><span class="cx"> outCalendar = yield outHome.calendarWithName(name)
</span><span class="cx"> try:
</span><del>- yield _migrateCalendar(calendar, outCalendar, getComponent,
- merge=merge)
</del><ins>+ yield _migrateCalendar(calendar, outCalendar, getComponent, merge=merge)
</ins><span class="cx"> except InternalDataStoreError:
</span><span class="cx"> log.error(
</span><span class="cx"> " Failed to migrate calendar: %s/%s" % (inHome.name(), name,)
</span><span class="lines">@@ -408,6 +406,11 @@
</span><span class="cx"> # No migration for notifications, since they weren't present in earlier
</span><span class="cx"> # released versions of CalendarServer.
</span><span class="cx">
</span><ins>+ # May need to create inbox if it was not present in the original file store for some reason
+ inboxCalendar = yield outHome.calendarWithName("inbox")
+ if inboxCalendar is None:
+ yield outHome.createCalendarWithName("inbox")
+
</ins><span class="cx"> # May need to split calendars by component type
</span><span class="cx"> if config.RestrictCalendarsToOneComponentType:
</span><span class="cx"> yield outHome.splitCalendars()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/sql.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/sql.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/sql.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -337,6 +337,8 @@
</span><span class="cx">
</span><span class="cx"> AddressBookHome._register(EADDRESSBOOKTYPE)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AddressBookSharingMixIn(SharingMixIn):
</span><span class="cx"> """
</span><span class="cx"> Sharing code shared between AddressBook and AddressBookObject
</span><span class="lines">@@ -359,7 +361,7 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _isSharedOrInvited(self):
</span><span class="cx"> """
</span><del>- return a bool if this L{AddressBook} is shared or invited
</del><ins>+ return True if this L{AddressBook} is shared or invited
</ins><span class="cx"> """
</span><span class="cx"> sharedRows = []
</span><span class="cx"> if self.owned():
</span><span class="lines">@@ -1103,7 +1105,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def updateShare(self, shareeView, mode=None, status=None, message=None, name=None):
</del><ins>+ def updateShare(self, shareeView, mode=None, status=None, message=None):
</ins><span class="cx"> """
</span><span class="cx"> Update share mode, status, and message for a home child shared with
</span><span class="cx"> this (owned) L{CommonHomeChild}.
</span><span class="lines">@@ -1124,9 +1126,6 @@
</span><span class="cx"> will be used as the default display name, or None to not update
</span><span class="cx"> @type message: L{str}
</span><span class="cx">
</span><del>- @param name: The bind resource name or None to not update
- @type message: L{str}
-
</del><span class="cx"> @return: the name of the shared item in the sharee's home.
</span><span class="cx"> @rtype: a L{Deferred} which fires with a L{str}
</span><span class="cx"> """
</span><span class="lines">@@ -1138,8 +1137,7 @@
</span><span class="cx"> columnMap = dict([(k, v if v != "" else None)
</span><span class="cx"> for k, v in {bind.BIND_MODE:mode,
</span><span class="cx"> bind.BIND_STATUS:status,
</span><del>- bind.MESSAGE:message,
- bind.RESOURCE_NAME:name}.iteritems() if v is not None])
</del><ins>+ bind.MESSAGE:message}.iteritems() if v is not None])
</ins><span class="cx">
</span><span class="cx"> if len(columnMap):
</span><span class="cx">
</span><span class="lines">@@ -1481,11 +1479,6 @@
</span><span class="cx"> self._initFromRow(tuple(rows[0]))
</span><span class="cx">
</span><span class="cx"> if self._kind == _ABO_KIND_GROUP:
</span><del>- # generate "X-ADDRESSBOOKSERVER-MEMBER" properties
- # calc md5 and set size
- componentText = str((yield self.component()))
- self._md5 = hashlib.md5(componentText).hexdigest()
- self._size = len(componentText)
</del><span class="cx">
</span><span class="cx"> groupBindRows = yield AddressBookObject._bindForResourceIDAndHomeID.on(
</span><span class="cx"> self._txn, resourceID=self._resourceID, homeID=self._home._resourceID
</span><span class="lines">@@ -1791,6 +1784,7 @@
</span><span class="cx"> uid = component.resourceUID()
</span><span class="cx"> assert inserting or self._uid == uid # can't change UID. Should be checked in upper layers
</span><span class="cx"> self._uid = uid
</span><ins>+ originalComponentText = str(component)
</ins><span class="cx">
</span><span class="cx"> if self._kind == _ABO_KIND_GROUP:
</span><span class="cx"> memberAddresses = set(component.resourceMemberAddresses())
</span><span class="lines">@@ -1828,33 +1822,27 @@
</span><span class="cx"> # missing uids and other cuaddrs e.g. user@example.com, are stored in same schema table
</span><span class="cx"> foreignMemberAddrs.extend(["urn:uuid:" + missingUID for missingUID in missingUIDs])
</span><span class="cx">
</span><del>- # don't store group members in object text
- orginialComponentText = str(component)
</del><ins>+ # sort unique members
</ins><span class="cx"> component.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
</span><span class="cx"> for memberAddress in sorted(list(memberAddresses)): # sort unique
</span><span class="cx"> component.addProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
</span><del>-
- # use sorted for md5
</del><span class="cx"> componentText = str(component)
</span><del>- self._md5 = hashlib.md5(componentText).hexdigest()
- self._componentChanged = orginialComponentText != componentText
</del><span class="cx">
</span><del>- # remove members from component get new text
- self._component = deepcopy(component)
- component.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
- componentText = str(component)
- self._objectText = componentText
-
- #size for quota does not include group members
- self._size = len(componentText)
-
</del><ins>+ # remove unneeded fields to get stored _objectText
+ thinComponent = deepcopy(component)
+ thinComponent.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+ thinComponent.removeProperties("X-ADDRESSBOOKSERVER-KIND")
+ thinComponent.removeProperties("UID")
+ self._objectText = str(thinComponent)
</ins><span class="cx"> else:
</span><del>- self._component = component
</del><span class="cx"> componentText = str(component)
</span><del>- self._md5 = hashlib.md5(componentText).hexdigest()
- self._size = len(componentText)
</del><span class="cx"> self._objectText = componentText
</span><span class="cx">
</span><ins>+ self._size = len(self._objectText)
+ self._component = component
+ self._md5 = hashlib.md5(componentText).hexdigest()
+ self._componentChanged = originalComponentText != componentText
+
</ins><span class="cx"> # Special - if migrating we need to preserve the original md5
</span><span class="cx"> if self._txn._migrating and hasattr(component, "md5"):
</span><span class="cx"> self._md5 = component.md5
</span><span class="lines">@@ -2031,6 +2019,8 @@
</span><span class="cx"> # now add the properties to the component
</span><span class="cx"> for memberAddress in sorted(memberAddresses + foreignMembers):
</span><span class="cx"> component.addProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
</span><ins>+ component.addProperty(Property("X-ADDRESSBOOKSERVER-KIND", "group"))
+ component.addProperty(Property("UID", self._uid))
</ins><span class="cx">
</span><span class="cx"> self._component = component
</span><span class="cx">
</span><span class="lines">@@ -2284,7 +2274,7 @@
</span><span class="cx"> else:
</span><span class="cx"> if status == _BIND_STATUS_ACCEPTED:
</span><span class="cx"> shareeView = yield shareeHome.objectWithShareUID(bindName)
</span><del>- yield shareeView._initSyncToken()
</del><ins>+ yield shareeView.addressbook()._initSyncToken()
</ins><span class="cx"> yield shareeView._initBindRevision()
</span><span class="cx">
</span><span class="cx"> queryCacher = self._txn._queryCacher
</span><span class="lines">@@ -2299,16 +2289,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def _initSyncToken(self):
- yield self.addressbook()._initSyncToken()
-
-
- @inlineCallbacks
</del><span class="cx"> def _initBindRevision(self):
</span><span class="cx"> yield self.addressbook()._initBindRevision()
</span><span class="cx">
</span><del>- # almost works
- # yield super(AddressBookObject, self)._initBindRevision()
</del><span class="cx"> bind = self._bindSchema
</span><span class="cx"> yield self._updateBindColumnsQuery(
</span><span class="cx"> {bind.BIND_REVISION : Parameter("revision"), }).on(
</span><span class="lines">@@ -2321,8 +2304,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- # TODO: This is almost the same as AddressBook.updateShare(): combine
- def updateShare(self, shareeView, mode=None, status=None, message=None, name=None):
</del><ins>+ def updateShare(self, shareeView, mode=None, status=None, message=None):
</ins><span class="cx"> """
</span><span class="cx"> Update share mode, status, and message for a home child shared with
</span><span class="cx"> this (owned) L{CommonHomeChild}.
</span><span class="lines">@@ -2343,9 +2325,6 @@
</span><span class="cx"> will be used as the default display name, or None to not update
</span><span class="cx"> @type message: L{str}
</span><span class="cx">
</span><del>- @param name: The bind resource name or None to not update
- @type message: L{str}
-
</del><span class="cx"> @return: the name of the shared item in the sharee's home.
</span><span class="cx"> @rtype: a L{Deferred} which fires with a L{str}
</span><span class="cx"> """
</span><span class="lines">@@ -2357,8 +2336,7 @@
</span><span class="cx"> columnMap = dict([(k, v if v != "" else None)
</span><span class="cx"> for k, v in {bind.BIND_MODE:mode,
</span><span class="cx"> bind.BIND_STATUS:status,
</span><del>- bind.MESSAGE:message,
- bind.RESOURCE_NAME:name}.iteritems() if v is not None])
</del><ins>+ bind.MESSAGE:message}.iteritems() if v is not None])
</ins><span class="cx">
</span><span class="cx"> if len(columnMap):
</span><span class="cx">
</span><span class="lines">@@ -2384,7 +2362,7 @@
</span><span class="cx"> shareeView._bindStatus = columnMap[bind.BIND_STATUS]
</span><span class="cx"> if shareeView._bindStatus == _BIND_STATUS_ACCEPTED:
</span><span class="cx"> if 0 == previouslyAcceptedBindCount:
</span><del>- yield shareeView._initSyncToken()
</del><ins>+ yield shareeView.addressbook()._initSyncToken()
</ins><span class="cx"> yield shareeView._initBindRevision()
</span><span class="cx"> shareeView.viewerHome()._children[self.addressbook().shareeName()] = shareeView.addressbook()
</span><span class="cx"> shareeView.viewerHome()._children[shareeView._resourceID] = shareeView.addressbook()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcarddavdatastoretestcommonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/test/common.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/test/common.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/carddav/datastore/test/common.py        2013-11-01 22:25:30 UTC (rev 11871)
</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("/CardDAV/example.com/home1/" 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("/CardDAV/example.com/home1/" 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"> """
</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"> """
</span><span class="lines">@@ -808,9 +805,7 @@
</span><span class="cx"> addressbookObject = yield addressbook1.addressbookObjectWithName("1.vcf")
</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"> """
</span><span class="cx"> Verify that the given object has a properties method that returns an
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/file.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/file.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/file.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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("cache", 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("push", 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"> """
</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("cache", 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("push", 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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -29,9 +29,10 @@
</span><span class="cx">
</span><span class="cx"> from pycalendar.datetime import PyCalendarDateTime
</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"> """
</span><span class="cx"> args = ["%s" % (arg,) for arg in args]
</span><span class="cx"> args = [((arg[:10] + "...") if len(arg) > 40 else arg) for arg in args]
</span><del>- self.statements.append(["%s %s" % (sql, args,), 0, 0])
</del><ins>+ self.statements.append(["%s %s" % (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"> """
</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"> """
</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("*** SQL Stats ***\n")
</span><span class="cx"> toFile.write("\n")
</span><span class="cx"> toFile.write("Label: %s\n" % (self.label,))
</span><span class="cx"> toFile.write("Unique statements: %d\n" % (len(set([statement[0] for statement in self.statements]),),))
</span><del>- toFile.write("Total statements: %d\n" % (len(self.statements),))
- toFile.write("Total rows: %d\n" % (sum([statement[1] for statement in self.statements]),))
- toFile.write("Total time (ms): %.3f\n" % (sum([statement[2] for statement in self.statements]) * 1000.0,))
- for sql, rows, t in self.statements:
</del><ins>+ toFile.write("Total statements: %d\n" % (total_statements,))
+ toFile.write("Total rows: %d\n" % (total_rows,))
+ toFile.write("Total time (ms): %.3f\n" % (total_time,))
+ t_last_end = self.startTime
+ for sql, rows, t_taken, t_end in self.statements:
</ins><span class="cx"> toFile.write("\n")
</span><span class="cx"> toFile.write("SQL: %s\n" % (sql,))
</span><span class="cx"> toFile.write("Rows: %s\n" % (rows,))
</span><del>- toFile.write("Time (ms): %.3f\n" % (t * 1000.0,))
</del><ins>+ toFile.write("Time (ms): %.3f\n" % (t_taken * 1000.0,))
+ toFile.write("Idle (ms): %.3f\n" % ((t_end - t_taken - t_last_end) * 1000.0,))
+ toFile.write("Elapsed (ms): %.3f\n" % ((t_end - self.startTime) * 1000.0,))
+ t_last_end = t_end
+ toFile.write("Commit (ms): %.3f\n" % ((time.time() - t_last_end) * 1000.0,))
</ins><span class="cx"> toFile.write("***\n\n")
</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"> """
</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"> """
</span><span class="cx"> Enqueue a L{twext.enterprise.queue.WorkItem} for later execution.
</span><span class="lines">@@ -550,14 +567,6 @@
</span><span class="cx"> ).on(self)
</span><span class="cx">
</span><span class="cx">
</span><del>- def calendarHomeWithUID(self, uid, create=False):
- return self.homeWithUID(ECALENDARTYPE, uid, create=create)
-
-
- def addressbookHomeWithUID(self, uid, create=False):
- return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
-
-
</del><span class="cx"> def _determineMemo(self, storeType, uid, create=False): #@UnusedVariable
</span><span class="cx"> """
</span><span class="cx"> Determine the memo dictionary to use for homeWithUID.
</span><span class="lines">@@ -591,6 +600,14 @@
</span><span class="cx"> return self._homeClass[storeType].homeWithUID(self, uid, create)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def calendarHomeWithUID(self, uid, create=False):
+ return self.homeWithUID(ECALENDARTYPE, uid, create=create)
+
+
+ def addressbookHomeWithUID(self, uid, create=False):
+ return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def homeWithResourceID(self, storeType, rid, create=False):
</span><span class="cx"> """
</span><span class="lines">@@ -1029,8 +1046,10 @@
</span><span class="cx"> """
</span><span class="cx"> Commit the transaction and execute any post-commit hooks.
</span><span class="cx"> """
</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):
+ """
+ Print the stats report and record log items
+ """
+ sql_statements, sql_rows, sql_time = self._stats.printReport()
+ self.logItems["sql-s"] = str(sql_statements)
+ self.logItems["sql-r"] = str(sql_rows)
+ self.logItems["sql-t"] = "%.1f" % (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("select pg_advisory_lock(1)")
</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("select pg_advisory_unlock(1)")
</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("resourceID")
+ )
+
+
+ @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"> """
</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"> """
</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("cache", 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("push", 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"> """
</span><span class="cx"> DAL query for (resource, deleted-flag)
</span><span class="cx"> """
</span><span class="cx"> rev = cls._revisionsSchema
</span><del>- return Select([rev.RESOURCE_NAME, rev.DELETED],
- From=rev,
- Where=(rev.REVISION > Parameter("revision")).And(
- rev.RESOURCE_ID == Parameter("resourceID")))
</del><ins>+ where = (rev.REVISION > Parameter("revision")).And(rev.RESOURCE_ID == Parameter("resourceID"))
+ 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"> """
</span><span class="cx">
</span><span class="cx"> results = [
</span><del>- (name if name else "", deleted)
- for name, deleted in
- (yield self._objectNamesSinceRevisionQuery.on(
- self._txn, revision=revision, resourceID=self._resourceID))
</del><ins>+ (name if name else "", 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"> """
</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"> """
</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("resourceID")).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">@@ -2931,7 +2992,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def updateShareFromSharingInvitation(self, invitation, mode=None, status=None, message=None, name=None):
</del><ins>+ def updateShareFromSharingInvitation(self, invitation, mode=None, status=None, message=None):
</ins><span class="cx"> """
</span><span class="cx"> Like L{updateShare} except that the original invitation is provided. That is used
</span><span class="cx"> to find the actual sharee L{CommonHomeChild} which is then passed to L{updateShare}.
</span><span class="lines">@@ -2944,12 +3005,12 @@
</span><span class="cx"> if shareeView is None:
</span><span class="cx"> shareeView = yield shareeHome.invitedObjectWithShareUID(invitation.uid())
</span><span class="cx">
</span><del>- result = yield self.updateShare(shareeView, mode, status, message, name)
</del><ins>+ result = yield self.updateShare(shareeView, mode, status, message)
</ins><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def updateShare(self, shareeView, mode=None, status=None, message=None, name=None):
</del><ins>+ def updateShare(self, shareeView, mode=None, status=None, message=None):
</ins><span class="cx"> """
</span><span class="cx"> Update share mode, status, and message for a home child shared with
</span><span class="cx"> this (owned) L{CommonHomeChild}.
</span><span class="lines">@@ -2970,9 +3031,6 @@
</span><span class="cx"> will be used as the default display name, or None to not update
</span><span class="cx"> @type message: L{str}
</span><span class="cx">
</span><del>- @param name: The bind resource name or None to not update
- @type message: L{str}
-
</del><span class="cx"> @return: the name of the shared item in the sharee's home.
</span><span class="cx"> @rtype: a L{Deferred} which fires with a L{str}
</span><span class="cx"> """
</span><span class="lines">@@ -2984,8 +3042,7 @@
</span><span class="cx"> columnMap = dict([(k, v if v != "" else None)
</span><span class="cx"> for k, v in {bind.BIND_MODE:mode,
</span><span class="cx"> bind.BIND_STATUS:status,
</span><del>- bind.MESSAGE:message,
- bind.RESOURCE_NAME:name}.iteritems() if v is not None])
</del><ins>+ bind.MESSAGE:message}.iteritems() if v is not None])
</ins><span class="cx">
</span><span class="cx"> if len(columnMap):
</span><span class="cx">
</span><span class="lines">@@ -3016,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">@@ -3074,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">@@ -3339,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">@@ -3519,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">@@ -3559,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"> """
</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">@@ -3741,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">@@ -3774,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">@@ -4260,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("cache", 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("push", 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">@@ -4484,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">@@ -5081,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"> """
</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("cache", 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("push", None)
+ if notifier:
+ yield notifier.notify(self._txn)
+
</ins><span class="cx"> returnValue(None)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">Property changes on: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql.py
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmergeinfo"></a>
<div class="delfile"><h4>Deleted: svn:mergeinfo</h4></div>
<span class="cx">/CalendarServer/branches/config-separation/txdav/common/datastore/sql.py:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351/txdav/common/datastore/sql.py:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py:6167
</span><span class="cx">/CalendarServer/branches/new-store/txdav/common/datastore/sql.py:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile/txdav/common/datastore/sql.py:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2/txdav/common/datastore/sql.py:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev/txdav/common/datastore/sql.py:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/common/datastore/sql.py:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/common/datastore/sql.py:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/common/datastore/sql.py:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/common/datastore/sql.py:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/common/datastore/sql.py:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim/txdav/common/datastore/sql.py:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments/txdav/common/datastore/sql.py:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/common/datastore/sql.py:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464/txdav/common/datastore/sql.py:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods/txdav/common/datastore/sql.py:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard/txdav/common/datastore/sql.py:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/common/datastore/sql.py:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/common/datastore/sql.py:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/common/datastore/sql.py:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones/txdav/common/datastore/sql.py:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/common/datastore/sql.py:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3/txdav/common/datastore/sql.py:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error/txdav/common/datastore/sql.py:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/common/datastore/sql.py:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit/txdav/common/datastore/sql.py:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/common/datastore/sql.py:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify/txdav/common/datastore/sql.py:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect/txdav/common/datastore/sql.py:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild/txdav/common/datastore/sql.py:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux/txdav/common/datastore/sql.py:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota/txdav/common/datastore/sql.py:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/common/datastore/sql.py:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html/txdav/common/datastore/sql.py:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client/txdav/common/datastore/sql.py:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests/txdav/common/datastore/sql.py:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge/txdav/common/datastore/sql.py:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/common/datastore/sql.py:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/common/datastore/sql.py:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete/txdav/common/datastore/sql.py:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export/txdav/common/datastore/sql.py:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api/txdav/common/datastore/sql.py:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle/txdav/common/datastore/sql.py:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls/txdav/common/datastore/sql.py:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html/txdav/common/datastore/sql.py:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim/txdav/common/datastore/sql.py:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/common/datastore/sql.py:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/common/datastore/sql.py:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q/txdav/common/datastore/sql.py:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing/txdav/common/datastore/sql.py:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota/txdav/common/datastore/sql.py:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport/txdav/common/datastore/sql.py:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/common/datastore/sql.py:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2/txdav/common/datastore/sql.py:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool/txdav/common/datastore/sql.py:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api/txdav/common/datastore/sql.py:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/common/datastore/sql.py:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store/txdav/common/datastore/sql.py:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop/txdav/common/datastore/sql.py:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions/txdav/common/datastore/sql.py:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias/txdav/common/datastore/sql.py:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport/txdav/common/datastore/sql.py:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked/txdav/common/datastore/sql.py:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted/txdav/common/datastore/sql.py:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize/txdav/common/datastore/sql.py:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups/txdav/common/datastore/sql.py:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files/txdav/common/datastore/sql.py:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush/txdav/common/datastore/sql.py:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems/txdav/common/datastore/sql.py:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources/txdav/common/datastore/sql.py:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2/txdav/common/datastore/sql.py:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events/txdav/common/datastore/sql.py:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/common/datastore/sql.py:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/common/datastore/sql.py:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2/txdav/common/datastore/sql.py:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing/txdav/common/datastore/sql.py:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations/txdav/common/datastore/sql.py:5515-5593
</span><a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemacurrentoracledialectsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current-oracle-dialect.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current-oracle-dialect.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -218,13 +218,13 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="lines">@@ -268,13 +268,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -288,7 +288,7 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</ins><span class="cx"> "ADDRESSBOOK_NAME" nvarchar2(255) default null,
</span><span class="cx"> "RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "REVISION" integer not null,
</span><span class="lines">@@ -365,7 +365,7 @@
</span><span class="cx"> "VALUE" nvarchar2(255)
</span><span class="cx"> );
</span><span class="cx">
</span><del>-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
</del><ins>+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '26');
</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">@@ -423,7 +423,7 @@
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ABO_MEMBERS_ADDRESSBO_4effa879 on ABO_MEMBERS (
</span><span class="lines">@@ -447,9 +447,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">@@ -457,18 +459,20 @@
</span><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</del><ins>+create index ADDRESSBOOK_OBJECT_RE_2bfcf757 on ADDRESSBOOK_OBJECT_REVISIONS (
</ins><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
- RESOURCE_NAME
</del><ins>+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"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemacurrentsql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/current.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -398,19 +398,19 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID                        integer                        not null references ADDRESSBOOK_HOME,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ OWNER_HOME_RESOURCE_ID                         integer         not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> ADDRESSBOOK_RESOURCE_NAME                         varchar(255)         not null,
</span><span class="cx"> BIND_MODE                         integer         not null,        -- enum CALENDAR_BIND_MODE
</span><span class="cx"> BIND_STATUS                         integer         not null,        -- enum CALENDAR_BIND_STATUS
</span><span class="cx"> BIND_REVISION                                                         integer         default 0 not null,
</span><span class="cx"> MESSAGE                         text,                 -- FIXME: xml?
</span><span class="cx">
</span><del>- primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID), -- implicit index
</del><ins>+ primary key (ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID), -- implicit index
</ins><span class="cx"> unique (ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME) -- implicit index
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BIND_RESOURCE_ID on
</span><del>- SHARED_ADDRESSBOOK_BIND(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
</del><ins>+ SHARED_ADDRESSBOOK_BIND(OWNER_HOME_RESOURCE_ID);
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> ------------------------
</span><span class="lines">@@ -489,14 +489,14 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (        
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID                 integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> GROUP_RESOURCE_ID                         integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- GROUP_ADDRESSBOOK_RESOURCE_NAME        varchar(255) not null,
</del><ins>+ GROUP_ADDRESSBOOK_NAME                        varchar(255) not null,
</ins><span class="cx"> BIND_MODE                 integer not null, -- enum CALENDAR_BIND_MODE
</span><span class="cx"> BIND_STATUS                 integer not null, -- enum CALENDAR_BIND_STATUS
</span><span class="cx"> BIND_REVISION                                                 integer default 0 not null,
</span><span class="cx"> MESSAGE                 text, -- FIXME: xml?
</span><span class="cx">
</span><span class="cx"> primary key (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_RESOURCE_ID), -- implicit index
</span><del>- unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_NAME) -- implicit index
</del><ins>+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_NAME) -- implicit index
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RESOURCE_ID on
</span><span class="lines">@@ -526,8 +526,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">@@ -539,21 +539,21 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID                         integer                        not null references ADDRESSBOOK_HOME,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID integer         references ADDRESSBOOK_HOME,
</del><ins>+ OWNER_HOME_RESOURCE_ID                         integer         references ADDRESSBOOK_HOME,
</ins><span class="cx"> ADDRESSBOOK_NAME                         varchar(255)         default null,
</span><span class="cx"> RESOURCE_NAME                         varchar(255),
</span><span class="cx"> REVISION                         integer         default nextval('REVISION_SEQ') not null,
</span><span class="cx"> DELETED                         boolean         not null
</span><span class="cx"> );
</span><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
- on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
</del><ins>+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_HOME_RESOURCE_ID);
</ins><span class="cx">
</span><del>-create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_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><del>- on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, REVISION);
</del><ins>+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, REVISION);
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> -----------------------------------
</span><span class="lines">@@ -695,6 +695,6 @@
</span><span class="cx"> VALUE varchar(255)
</span><span class="cx"> );
</span><span class="cx">
</span><del>-insert into CALENDARSERVER values ('VERSION', '24');
</del><ins>+insert into CALENDARSERVER values ('VERSION', '26');
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv20sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v20.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -216,13 +216,13 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="lines">@@ -266,13 +266,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -286,7 +286,7 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</ins><span class="cx"> "ADDRESSBOOK_NAME" nvarchar2(255) default null,
</span><span class="cx"> "RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "REVISION" integer not null,
</span><span class="lines">@@ -403,7 +403,7 @@
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
</span><span class="lines">@@ -427,16 +427,16 @@
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> RESOURCE_NAME
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv21sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v21.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -216,13 +216,13 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="lines">@@ -266,13 +266,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -286,7 +286,7 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</ins><span class="cx"> "ADDRESSBOOK_NAME" nvarchar2(255) default null,
</span><span class="cx"> "RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "REVISION" integer not null,
</span><span class="lines">@@ -403,7 +403,7 @@
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
</span><span class="lines">@@ -427,16 +427,16 @@
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> RESOURCE_NAME
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv22sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v22.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -218,13 +218,13 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="lines">@@ -268,13 +268,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -288,7 +288,7 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</ins><span class="cx"> "ADDRESSBOOK_NAME" nvarchar2(255) default null,
</span><span class="cx"> "RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "REVISION" integer not null,
</span><span class="lines">@@ -405,7 +405,7 @@
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
</span><span class="lines">@@ -429,16 +429,16 @@
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> RESOURCE_NAME
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv23sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v23.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v23.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v23.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -218,13 +218,13 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="lines">@@ -268,13 +268,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create table CALENDAR_OBJECT_REVISIONS (
</span><span class="lines">@@ -288,7 +288,7 @@
</span><span class="cx">
</span><span class="cx"> create table ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer references ADDRESSBOOK_HOME,
</ins><span class="cx"> "ADDRESSBOOK_NAME" nvarchar2(255) default null,
</span><span class="cx"> "RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "REVISION" integer not null,
</span><span class="lines">@@ -411,7 +411,7 @@
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
</span><span class="lines">@@ -435,16 +435,16 @@
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> RESOURCE_NAME
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv24sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv24sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v24.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,491 @@
</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 (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "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" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "INSTANCE_ID" integer primary key,
+ "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" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+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" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '24');
+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_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+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_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldoracledialectv25sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaoldoracledialectv25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v25.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v25.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v25.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/oracle-dialect/v25.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,491 @@
</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 (
+ "HOSTNAME" nvarchar2(255),
+ "PID" integer not null,
+ "PORT" integer not null,
+ "TIME" timestamp default CURRENT_TIMESTAMP at time zone 'UTC' not null,
+ primary key("HOSTNAME", "PORT")
+);
+
+create table NAMED_LOCK (
+ "LOCK_NAME" nvarchar2(255) primary key
+);
+
+create table CALENDAR_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR (
+ "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+ "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" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "AVAILABILITY" nclob default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_METADATA (
+ "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+ "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table NOTIFICATION_HOME (
+ "RESOURCE_ID" integer primary key,
+ "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+ "RESOURCE_ID" integer primary key,
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+ "NOTIFICATION_UID" nvarchar2(255),
+ "XML_TYPE" nvarchar2(255),
+ "XML_DATA" nclob,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ "TRANSP" integer default 0 not null,
+ "ALARM_VEVENT_TIMED" nclob default null,
+ "ALARM_VEVENT_ALLDAY" nclob default null,
+ "ALARM_VTODO_TIMED" nclob default null,
+ "ALARM_VTODO_ALLDAY" nclob default null,
+ "TIMEZONE" nclob default null,
+ primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"),
+ unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "RESOURCE_ID" integer primary key,
+ "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob,
+ "ICALENDAR_UID" nvarchar2(255),
+ "ICALENDAR_TYPE" nvarchar2(255),
+ "ATTACHMENTS_MODE" integer default 0 not null,
+ "DROPBOX_ID" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "RECURRANCE_MIN" date,
+ "RECURRANCE_MAX" date,
+ "ACCESS" integer default 0 not null,
+ "SCHEDULE_OBJECT" integer default 0,
+ "SCHEDULE_TAG" nvarchar2(36) default null,
+ "SCHEDULE_ETAGS" nclob default null,
+ "PRIVATE_COMMENTS" integer default 0 not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "INSTANCE_ID" integer primary key,
+ "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" integer not null,
+ "START_DATE" timestamp not null,
+ "END_DATE" timestamp not null,
+ "FBTYPE" integer not null,
+ "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+ "USER_ID" nvarchar2(255),
+ "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+ "ATTACHMENT_ID" integer primary key,
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "DROPBOX_ID" nvarchar2(255),
+ "CONTENT_TYPE" nvarchar2(255),
+ "SIZE" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PATH" nvarchar2(1024)
+);
+
+create table ATTACHMENT_CALENDAR_OBJECT (
+ "ATTACHMENT_ID" integer not null references ATTACHMENT on delete cascade,
+ "MANAGED_ID" nvarchar2(255),
+ "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+ primary key("ATTACHMENT_ID", "CALENDAR_OBJECT_RESOURCE_ID"),
+ unique("MANAGED_ID", "CALENDAR_OBJECT_RESOURCE_ID")
+);
+
+create table RESOURCE_PROPERTY (
+ "RESOURCE_ID" integer not null,
+ "NAME" nvarchar2(255),
+ "VALUE" nclob,
+ "VIEWER_UID" nvarchar2(255),
+ primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_PROPERTY_STORE_ID" integer not null,
+ "OWNER_UID" nvarchar2(255) unique,
+ "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+ "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+ "QUOTA_USED_BYTES" integer default 0 not null,
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+ "RESOURCE_ID" integer primary key,
+ "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "VCARD_TEXT" nclob,
+ "VCARD_UID" nvarchar2(255),
+ "KIND" integer not null,
+ "MD5" nchar(32),
+ "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID")
+);
+
+create table ADDRESSBOOK_OBJECT_KIND (
+ "ID" integer primary key,
+ "DESCRIPTION" 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 (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ID" integer not null references ADDRESSBOOK_OBJECT,
+ primary key("GROUP_ID", "MEMBER_ID")
+);
+
+create table ABO_FOREIGN_MEMBERS (
+ "GROUP_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
+ "ADDRESSBOOK_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
+ "MEMBER_ADDRESS" nvarchar2(255),
+ primary key("GROUP_ID", "MEMBER_ADDRESS")
+);
+
+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" nvarchar2(255),
+ "BIND_MODE" integer not null,
+ "BIND_STATUS" integer not null,
+ "BIND_REVISION" integer default 0 not null,
+ "MESSAGE" nclob,
+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+ "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+ "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+ "CALENDAR_NAME" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+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" nvarchar2(255) default null,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+ "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+ "RESOURCE_NAME" nvarchar2(255),
+ "REVISION" integer not null,
+ "DELETED" integer not null,
+ unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+ "TOKEN" nvarchar2(255),
+ "RESOURCE_KEY" nvarchar2(255),
+ "MODIFIED" integer not null,
+ "SUBSCRIBER_GUID" nvarchar2(255),
+ "USER_AGENT" nvarchar2(255) default null,
+ "IP_ADDR" nvarchar2(255) default null,
+ primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table IMIP_TOKENS (
+ "TOKEN" nvarchar2(255),
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALUID" nvarchar2(255),
+ "ACCESSED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ primary key("ORGANIZER", "ATTENDEE", "ICALUID")
+);
+
+create table IMIP_INVITATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "FROM_ADDR" nvarchar2(255),
+ "TO_ADDR" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table IMIP_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table IMIP_REPLY_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "ORGANIZER" nvarchar2(255),
+ "ATTENDEE" nvarchar2(255),
+ "ICALENDAR_TEXT" nclob
+);
+
+create table PUSH_NOTIFICATION_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "PUSH_ID" nvarchar2(255)
+);
+
+create table GROUP_CACHER_POLLING_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR_OBJECT_SPLITTER_WORK (
+ "WORK_ID" integer primary key not null,
+ "NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+ "RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade
+);
+
+create table CALENDARSERVER (
+ "NAME" nvarchar2(255) primary key,
+ "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '25');
+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_2643d556 on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+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_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME
+);
+
+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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldpostgresdialectv24sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv24sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v24.sql        2013-11-01 22:25:30 UTC (rev 11871)
</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 "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+-----------------
+-- 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_ADDRESSBOOK_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_ADDRESSBOOK_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_ADDRESSBOOK_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_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, GROUP_RESOURCE_ID), -- implicit index
+ unique (ADDRESSBOOK_HOME_RESOURCE_ID, GROUP_ADDRESSBOOK_RESOURCE_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
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+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_ADDRESSBOOK_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_ADDRESSBOOK_HOME_RESOURCE_ID
+ on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, OWNER_ADDRESSBOOK_HOME_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_RESOURCE_NAME
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_OWNER_HOME_RESOURCE_ID_REVISION
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_ADDRESSBOOK_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', '24');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaoldpostgresdialectv25sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaoldpostgresdialectv25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v25.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v25.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v25.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/old/postgres-dialect/v25.sql        2013-11-01 22:25:30 UTC (rev 11871)
</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 "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+
+-----------------
+-- 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
+ on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+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
+ on ADDRESSBOOK_OBJECT_REVISIONS(OWNER_HOME_RESOURCE_ID, RESOURCE_NAME);
+
+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', '25');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_19_to_20sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_19_to_20.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -31,18 +31,18 @@
</span><span class="cx">
</span><span class="cx"> create table SHARED_ADDRESSBOOK_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><del>- "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</del><ins>+ "OWNER_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME on delete cascade,
</ins><span class="cx"> "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</span><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><del>- primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"),
</del><ins>+ primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "OWNER_HOME_RESOURCE_ID"),
</ins><span class="cx"> unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_ADDRESSBOOK_BI_e9a2e6d4 on SHARED_ADDRESSBOOK_BIND (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -55,13 +55,13 @@
</span><span class="cx"> create table SHARED_GROUP_BIND (
</span><span class="cx"> "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
</span><span class="cx"> "GROUP_RESOURCE_ID" integer not null references ADDRESSBOOK_OBJECT on delete cascade,
</span><del>- "GROUP_ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
</del><ins>+ "GROUP_ADDRESSBOOK_NAME" nvarchar2(255),
</ins><span class="cx"> "BIND_MODE" integer not null,
</span><span class="cx"> "BIND_STATUS" integer not null,
</span><span class="cx"> "BIND_REVISION" integer default 0 not null,
</span><span class="cx"> "MESSAGE" nclob,
</span><span class="cx"> primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_RESOURCE_ID"),
</span><del>- unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_RESOURCE_NAME")
</del><ins>+ unique("ADDRESSBOOK_HOME_RESOURCE_ID", "GROUP_ADDRESSBOOK_NAME")
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index SHARED_GROUP_BIND_RES_cf52f95d on SHARED_GROUP_BIND (
</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 ("ADDRESSBOOK_PROPERTY_STORE_ID" integer not null);
</del><ins>+        add ("ADDRESSBOOK_PROPERTY_STORE_ID" 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,14 +137,17 @@
</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 ("ADDRESSBOOK_PROPERTY_STORE_ID" not null);
+
+
</ins><span class="cx"> --------------------------------
</span><span class="cx"> -- change ADDRESSBOOK_OBJECT --
</span><span class="cx"> --------------------------------
</span><span class="cx">
</span><span class="cx"> alter table ADDRESSBOOK_OBJECT
</span><del>-        add ("KIND"        integer); -- enum ADDRESSBOOK_OBJECT_KIND
</del><ins>+        add ("KIND"        integer) -- enum ADDRESSBOOK_OBJECT_KIND
</ins><span class="cx">         add ("ADDRESSBOOK_HOME_RESOURCE_ID"        integer        references ADDRESSBOOK_HOME on delete cascade);
</span><span class="cx">
</span><span class="cx"> update ADDRESSBOOK_OBJECT
</span><span class="lines">@@ -176,24 +183,25 @@
</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 ("KIND" not null,
- "ADDRESSBOOK_HOME_RESOURCE_ID" not null)
-        drop ("ADDRESSBOOK_RESOURCE_ID");
</del><ins>+ modify ("KIND" not null)
+ modify ("ADDRESSBOOK_HOME_RESOURCE_ID" 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 ("ADDRESSBOOK_HOME_RESOURCE_ID", "RESOURCE_NAME")
</span><del>-         unique ("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID");
</del><ins>+        add unique ("ADDRESSBOOK_HOME_RESOURCE_ID", "VCARD_UID");
</ins><span class="cx">
</span><span class="cx"> ------------------------------------------
</span><span class="cx"> -- change ADDRESSBOOK_OBJECT_REVISIONS --
</span><span class="cx"> ------------------------------------------
</span><span class="cx">
</span><span class="cx"> alter table ADDRESSBOOK_OBJECT_REVISIONS
</span><del>-        add ("OWNER_ADDRESSBOOK_HOME_RESOURCE_ID"        integer        references ADDRESSBOOK_HOME);
</del><ins>+        add ("OWNER_HOME_RESOURCE_ID"        integer        references ADDRESSBOOK_HOME);
</ins><span class="cx">
</span><span class="cx"> update ADDRESSBOOK_OBJECT_REVISIONS
</span><del>-        set        OWNER_ADDRESSBOOK_HOME_RESOURCE_ID = (
</del><ins>+        set        OWNER_HOME_RESOURCE_ID = (
</ins><span class="cx">                 select ADDRESSBOOK_HOME_RESOURCE_ID
</span><span class="cx">                         from ADDRESSBOOK_BIND
</span><span class="cx">                 where
</span><span class="lines">@@ -229,16 +237,16 @@
</span><span class="cx"> -- New indexes
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_40cc2d73 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><span class="cx"> ADDRESSBOOK_HOME_RESOURCE_ID,
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID
</del><ins>+ OWNER_HOME_RESOURCE_ID
</ins><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_980b9872 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> RESOURCE_NAME
</span><span class="cx"> );
</span><span class="cx">
</span><span class="cx"> create index ADDRESSBOOK_OBJECT_RE_45004780 on ADDRESSBOOK_OBJECT_REVISIONS (
</span><del>- OWNER_ADDRESSBOOK_HOME_RESOURCE_ID,
</del><ins>+ OWNER_HOME_RESOURCE_ID,
</ins><span class="cx"> REVISION
</span><span class="cx"> );
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_24_to_25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_24_to_25.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- This is actually a noop for Oracle as we had some invalid names in the v20 schema that
+-- were corrected in v20 (but not corrected in postgres which is being updated for v25).
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaupgradesoracledialectupgrade_from_25_to_26sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_25_to_26.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 25 to 26 --
+---------------------------------------------------
+
+-- Replace index
+
+drop index CALENDAR_OBJECT_REVIS_2643d556;
+create index CALENDAR_OBJECT_REVIS_6d9d929c on CALENDAR_OBJECT_REVISIONS (
+ CALENDAR_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+
+drop index ADDRESSBOOK_OBJECT_RE_980b9872;
+create index ADDRESSBOOK_OBJECT_RE_00fe8288 on ADDRESSBOOK_OBJECT_REVISIONS (
+ OWNER_HOME_RESOURCE_ID,
+ RESOURCE_NAME,
+ DELETED,
+ REVISION
+);
+
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_13_to_14sql"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_13_to_14.sql        2013-11-01 22:25:30 UTC (rev 11871)
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_24_to_25sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_24_to_25.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 24 to 25 --
+---------------------------------------------------
+
+-- Rename columns and indexes
+alter table SHARED_ADDRESSBOOK_BIND
+        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter table SHARED_GROUP_BIND
+        rename column GROUP_ADDRESSBOOK_RESOURCE_NAME to GROUP_ADDRESSBOOK_NAME;
+
+alter table ADDRESSBOOK_OBJECT_REVISIONS
+        rename column OWNER_ADDRESSBOOK_HOME_RESOURCE_ID to OWNER_HOME_RESOURCE_ID;
+
+alter index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_ADDRESSBOOK_HOME_RESOURCE_ID rename to ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_OWNER_HOME_RESOURCE_ID;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '25' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sqlfromrev11870CalendarServertrunktxdavcommondatastoresql_schemaupgradespostgresdialectupgrade_from_25_to_26sql"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql (from rev 11870, CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql) (0 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql         (rev 0)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_25_to_26.sql        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+----
+-- Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+----
+
+---------------------------------------------------
+-- Upgrade database schema from VERSION 25 to 26 --
+---------------------------------------------------
+
+-- 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);
+
+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);
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '26' where NAME = 'VERSION';
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoresql_tablespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_tables.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_tables.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/sql_tables.py        2013-11-01 22:25:30 UTC (rev 11871)
</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_ADDRESSBOOK_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_RESOURCE_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_ADDRESSBOOK_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"> """
</span><span class="cx"> Combine two table dictionaries used in a join to produce a single dictionary
</span><span class="lines">@@ -291,6 +275,10 @@
</span><span class="cx"> first = False
</span><span class="cx"> else:
</span><span class="cx"> out.write(",\n")
</span><ins>+
+ if len(column.model.name) > ORACLE_TABLE_NAME_MAX:
+ raise SchemaBroken("Column name too long: %s" % (column.model.name,))
+
</ins><span class="cx"> typeName = column.model.type.name
</span><span class="cx"> typeName = _translatedTypes.get(typeName, typeName)
</span><span class="cx"> out.write(' "%s" %s' % (column.model.name, typeName))
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoretestutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/test/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/test/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/test/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -726,7 +726,7 @@
</span><span class="cx"> return "/%s/%s/%s/" % (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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqltesttest_upgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/test/test_upgrade.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/test/test_upgrade.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/test/test_upgrade.py        2013-11-01 22:25:30 UTC (rev 11871)
</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("RuntimeError not raised")
</del><ins>+ self.fail("NotAllowedToUpgrade not raised")
</ins><span class="cx"> else:
</span><del>- self.fail("RuntimeError not raised")
</del><ins>+ self.fail("NotAllowedToUpgrade not raised")
</ins><span class="cx"> new_version = yield _loadVersion()
</span><span class="cx"> yield _unloadOldSchema()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrade.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrade.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrade.py        2013-11-01 22:25:30 UTC (rev 11871)
</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):
+ """
+ Exception indicating an upgrade is needed but we're not configured to
+ perform it.
+ """
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class UpgradeDatabaseCoreStep(object):
</span><span class="cx"> """
</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("Database upgrade is needed but not allowed.")
</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="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradesaddressbook_upgrade_from_1_to_2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/addressbook_upgrade_from_1_to_2.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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("Starting Addressbook Populate Members")
+
</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, "Populate Members")
</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("Starting Addressbook Remove Resource Type")
+
</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("End Addressbook Remove Resource Type")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_1_to_2py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_1_to_2.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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"> """
</span><span class="cx">
</span><ins>+ logUpgradeStatus("Starting Move supported-component-set")
+
</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 = ",".join(sorted([comp.attributes["name"].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("Move supported-component-set", 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("End Move supported-component-set")
</ins><span class="cx"> except RuntimeError:
</span><span class="cx"> yield sqlTxn.abort()
</span><ins>+ logUpgradeError(
+ "Move supported-component-set",
+ "Last calendar: {}".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("Starting Split Calendars")
+
</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, "Split Calendars")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_3_to_4.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</span><span class="cx"> Data upgrade from database version 3 to 4
</span><span class="lines">@@ -41,165 +39,111 @@
</span><span class="cx"> """
</span><span class="cx"> Do the required upgrade steps.
</span><span class="cx"> """
</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"> """
</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"> """
</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, "Update Calendar Home", 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"> """
</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"> """
</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) > 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("/")
- if len(parts) == 5:
</del><ins>+@inlineCallbacks
+def moveDefaultCalendarProperties(home):
+ """
+ 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.
+ """
</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:
- yield calendarHome.setDefaultCalendar(
- calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
- )
</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("ids", 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):
+ """
+ Move the specified property value to the matching CALENDAR_HOME_METADATA table column.
+ """
</ins><span class="cx">
</span><del>- yield cleanPropertyStore()
</del><ins>+ inbox = (yield home.calendarWithName("inbox"))
+ prop = inbox.properties().get(PropertyName.fromElement(propname))
+ if prop is not None:
+ defaultCalendar = str(prop.children[0])
+ parts = defaultCalendar.split("/")
+ 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:
+ yield home.setDefaultCalendar(
+ calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
+ )
+ 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"> """
</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"> """
</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():
+ continue
+ 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)]
+ inbox = (yield home.calendarWithName("inbox"))
+ prop = inbox.properties().get(PropertyName.fromElement(caldavxml.CalendarFreeBusySet))
+ if prop is not None:
+ del inbox.properties()[PropertyName.fromElement(caldavxml.CalendarFreeBusySet)]
</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("ids", 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"> """
</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">@@ -207,25 +151,25 @@
</span><span class="cx"> """
</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">@@ -234,90 +178,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"> """
</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"> """
</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) > 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("ids", 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("Starting Calendar Remove Resource Type")
+
</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("End Calendar Remove Resource Type")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradescalendar_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/calendar_upgrade_from_4_to_5.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</span><span class="cx"> Data upgrade from database version 4 to 5
</span><span class="lines">@@ -43,136 +40,75 @@
</span><span class="cx"> """
</span><span class="cx"> Do the required upgrade steps.
</span><span class="cx"> """
</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"> """
</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"> """
</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, "Update Calendar Home", 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):
+ """
+ For this calendar home, update the associated properties on the home or its owned calendars.
+ """
</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("ids", 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):
+ """
+ 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.
+ """
</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"> """
</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"> """
</span><ins>+ inbox = (yield home.calendarWithName("inbox"))
+ 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("ids", 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("ids", 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"> """
</span><span class="lines">@@ -190,6 +126,8 @@
</span><span class="cx"> {http://twistedmatrix.com/xml_namespace/dav/}schedule-auto-respond
</span><span class="cx">
</span><span class="cx"> """
</span><ins>+ logUpgradeStatus("Starting Calendar Remove Other Properties")
+
</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 +143,5 @@
</span><span class="cx">
</span><span class="cx"> yield sqlTxn.commit()
</span><span class="cx"> yield cleanPropertyStore()
</span><ins>+
+ logUpgradeStatus("End Calendar Remove Other Properties")
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_3_to_4py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_3_to_4.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> """
</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"> """
</span><span class="lines">@@ -37,7 +41,7 @@
</span><span class="cx"> """
</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 ("user01", "user02",):
</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 ("user01", "user02",):
</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="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(home.isDefaultCalendar(calendar))
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", 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="calendar_1", home=user))
+ self.assertFalse(home.isDefaultCalendar(calendar))
+ inbox = (yield self.calendarUnderTest(name="inbox", 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(("user01", "user02",), ())
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialDefaultCalendarUpgrade(self):
+ yield self._defaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._defaultCalendarUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _invalidDefaultCalendarUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on inbox
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</span><ins>+ inbox.properties()[PropertyName.fromElement(ScheduleDefaultCalendarURL)] = ScheduleDefaultCalendarURL(HRef.fromString("/calendars/__uids__/%s/tasks_1" % (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("tasks_1"))
+ yield tasks.setSupportedComponents("VTODO")
+
+ # 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="tasks_1", home=user))
+ self.assertFalse(home.isDefaultCalendar(calendar))
+ inbox = (yield self.calendarUnderTest(name="inbox", 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="tasks_1", home=user))
+ self.assertFalse(home.isDefaultCalendar(calendar))
+ inbox = (yield self.calendarUnderTest(name="inbox", 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(("user01", "user02",), ())
+
+
+ @inlineCallbacks
+ def test_partialInvalidDefaultCalendarUpgrade(self):
+ yield self._invalidDefaultCalendarUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._invalidDefaultCalendarUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _calendarTranspUpgrade_setup(self):
+
+ # Set dead property on inbox
+ for user in ("user01", "user02",):
+ inbox = (yield self.calendarUnderTest(name="inbox", home=user))
</ins><span class="cx"> inbox.properties()[PropertyName.fromElement(CalendarFreeBusySet)] = CalendarFreeBusySet(HRef.fromString("/calendars/__uids__/%s/calendar_1" % (user,)))
</span><span class="cx">
</span><span class="cx"> # Force current to transparent
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", 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 == "user01" 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 ("user01", "user02",):
</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="calendar_1", home=user))
</span><del>- self.assertTrue(calendar.isUsedForFreeBusy())
</del><ins>+ if user == "user01":
+ 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="inbox", 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="calendar_1", home=user))
+ if user == "user01":
+ self.assertFalse(calendar.isUsedForFreeBusy())
+ else:
+ self.assertFalse(calendar.isUsedForFreeBusy())
+ self.assertTrue(PropertyName.fromElement(caldavxml.ScheduleCalendarTransp) in calendar.properties())
+ inbox = (yield self.calendarUnderTest(name="inbox", 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(("user01", "user02",), ())
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarTranspUpgrade(self):
+ yield self._calendarTranspUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+
+
+ @inlineCallbacks
+ def _defaultAlarmUpgrade_setup(self):
+
</ins><span class="cx"> alarmhome1 = """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="user02")
</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 ("user01", "user02",):
+ # 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="user01")
</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="user02")
- 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 "user02" in changed_users:
+ home = (yield self.homeUnderTest(name="user02"))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 4)
+ shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+ 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="user02"))
+ version = (yield home.dataVersion())
+ self.assertEqual(version, 3)
+ shared = yield self.calendarUnderTest(name=shared_name, home="user02")
+ 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(("user01", "user02",), (), 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, "user01")
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), 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(("user01", "user02",), ())
+ yield self._calendarTranspUpgrade_check(("user01", "user02",), ())
+ yield self._defaultAlarmUpgrade_check(("user01", "user02",), (), 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, "user01")
+ yield self._defaultCalendarUpgrade_check(("user01",), ("user02",))
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), 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 ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="lines">@@ -273,12 +468,60 @@
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
</span><ins>+
+ yield self.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "3")
+
</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 ("user01", "user02",):
- calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
- self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
</del><ins>+ if full:
+ for user in ("user01", "user02",):
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceType) not in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 4)
+ else:
+ for user in ("user01", "user02",):
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceType) in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ 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, "UpgradeHomePrefix", "")
+ 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(("user01", "user02",), ())
+ yield self._calendarTranspUpgrade_check(("user01", "user02",), ())
+ yield self._defaultAlarmUpgrade_check(("user01", "user02",), (), detailshome, detailscalendar, detailsshared, shared_name)
+ yield self._resourceTypeUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_partialFullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "user01")
+ 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(("user01",), ("user02",))
+ yield self._calendarTranspUpgrade_check(("user01",), ("user02",))
+ yield self._defaultAlarmUpgrade_check(("user01",), ("user02",), detailshome, detailscalendar, detailsshared, shared_name)
+ yield self._resourceTypeUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradestesttest_upgrade_from_4_to_5py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/test/test_upgrade_from_4_to_5.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</span><span class="cx"> Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
</span><span class="cx"> """
</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"> """
</span><span class="lines">@@ -35,7 +38,7 @@
</span><span class="cx"> """
</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("""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(("user01", "user02", "user03",), (), user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarTimezoneUpgrade(self):
+ user_details = yield self._calendarTimezoneUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details)
+
+
+ @inlineCallbacks
+ def _calendarAvailabilityUpgrade_setup(self):
+
</ins><span class="cx"> av1 = Component.fromString("""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="inbox", 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="inbox", 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="inbox", 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(("user01", "user02", "user03",), (), user_details)
</ins><span class="cx">
</span><ins>+
+ @inlineCallbacks
+ def test_partialCalendarAvailabilityUpgrade(self):
+ user_details = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), 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(("user01", "user02", "user03",), (), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01", "user02", "user03",), (), user_details2)
+
+
+ @inlineCallbacks
+ def test_partialCombinedUpgrade(self):
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield updateCalendarHomes(self._sqlCalendarStore, "user01")
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), user_details2)
+
+
+ @inlineCallbacks
+ def _removeOtherPropertiesUpgrade_setup(self):
+
</ins><span class="cx"> # Set dead property on calendar
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="lines">@@ -243,12 +325,55 @@
</span><span class="cx"> for user in ("user01", "user02",):
</span><span class="cx"> calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
</span><span class="cx"> self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
</span><ins>+
+ yield self.transactionUnderTest().updateCalendarserverValue("CALENDAR-DATAVERSION", "4")
+
</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 ("user01", "user02",):
</span><del>- calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
- self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
</del><ins>+ if full:
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceID) not in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ self.assertEqual(int(version), 5)
+ else:
+ calendar = (yield self.calendarUnderTest(name="calendar_1", home=user))
+ self.assertTrue(PropertyName.fromElement(element.ResourceID) in calendar.properties())
+ version = yield self.transactionUnderTest().calendarserverValue("CALENDAR-DATAVERSION")
+ 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, "UpgradeHomePrefix", "")
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield self._removeOtherPropertiesUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01", "user02", "user03",), (), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01", "user02", "user03",), (), user_details2)
+ yield self._removeOtherPropertiesUpgrade_check()
+
+
+ @inlineCallbacks
+ def test_partialFullUpgrade(self):
+ self.patch(config, "UpgradeHomePrefix", "user01")
+ user_details1 = yield self._calendarTimezoneUpgrade_setup()
+ user_details2 = yield self._calendarAvailabilityUpgrade_setup()
+ yield self._removeOtherPropertiesUpgrade_setup()
+ yield doUpgrade(self._sqlCalendarStore)
+ yield self._calendarTimezoneUpgrade_check(("user01",), ("user02", "user03",), user_details1)
+ yield self._calendarAvailabilityUpgrade_check(("user01",), ("user02", "user03",), user_details2)
+ yield self._removeOtherPropertiesUpgrade_check(False)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavcommondatastoreupgradesqlupgradesutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/util.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/util.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/common/datastore/upgrade/sql/upgrades/util.py        2013-11-01 22:25:30 UTC (rev 11871)
</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"> """
</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"> """
</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"> """
</span><span class="cx">
</span><ins>+ txn = store.newTransaction("updateDataVersion")
+ where = homeSchema.DATAVERSION < 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("updateDataVersion")
</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 < 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("End {}".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("Failed to upgrade %s to %s: %s" % (homeSchema, version, e))
</del><ins>+ logUpgradeError(
+ logStr,
+ "Failed to upgrade {} to {}: {}".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("Database upgrade {title}", title=title)
+ else:
+ divisor = 1000 if total > 1000 else 100
+ if (divmod(count, divisor)[1] == 0) or (count == total):
+ log.info("Database upgrade {title}: {count} of {total}", title=title, count=count, total=total)
+
+
+
+def logUpgradeError(title, details):
+ log.error("Database upgrade {title} failed: {details}", title=title, details=details)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavxmlbasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/base.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/base.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/base.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -693,7 +693,7 @@
</span><span class="cx"> return date.strftime("%a, %d %b %Y %H:%M:%S GMT")
</span><span class="cx">
</span><span class="cx"> if type(date) is int:
</span><del>- date = format(datetime.datetime.fromtimestamp(date))
</del><ins>+ date = format(datetime.datetime.utcfromtimestamp(date))
</ins><span class="cx"> elif type(date) is str:
</span><span class="cx"> pass
</span><span class="cx"> elif type(date) is unicode:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboofixnoischeduletxdavxmlrfc6578py"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/rfc6578.py (11870 => 11871)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/rfc6578.py        2013-11-01 21:46:19 UTC (rev 11870)
+++ CalendarServer/branches/users/cdaboo/fix-no-ischedule/txdav/xml/rfc6578.py        2013-11-01 22:25:30 UTC (rev 11871)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -51,7 +51,7 @@
</span><span class="cx"> allowed_children = {
</span><span class="cx"> (dav_namespace, "sync-token"): (0, 1), # When used in the REPORT this is required
</span><span class="cx"> (dav_namespace, "sync-level"): (0, 1), # When used in the REPORT this is required
</span><del>- (dav_namespace, "prop" ): (0, 1),
</del><ins>+ (dav_namespace, "prop"): (0, 1),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> def __init__(self, *children, **attributes):
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx"> self.property = None
</span><span class="cx"> self.sync_token = None
</span><span class="cx"> self.sync_level = None
</span><ins>+ self.sync_limit = None
</ins><span class="cx">
</span><span class="cx"> for child in self.children:
</span><span class="cx"> qname = child.qname()
</span><span class="lines">@@ -70,12 +71,20 @@
</span><span class="cx"> elif qname == (dav_namespace, "sync-level"):
</span><span class="cx"> self.sync_level = str(child)
</span><span class="cx">
</span><ins>+ elif qname == (dav_namespace, "limit"):
+ if len(child.children) == 1 and child.children[0].qname() == (dav_namespace, "nresults"):
+ try:
+ self.sync_limit = int(str(child.children[0]))
+ except TypeError:
+ pass
+
</ins><span class="cx"> elif qname == (dav_namespace, "prop"):
</span><span class="cx"> if self.property is not None:
</span><span class="cx"> raise ValueError("Only one of DAV:prop allowed")
</span><span class="cx"> self.property = child
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> @registerElementClass
</span><span class="cx"> class SyncToken (WebDAVTextElement):
</span><span class="lines">@@ -87,6 +96,7 @@
</span><span class="cx"> protected = True
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @registerElement
</span><span class="cx"> @registerElementClass
</span><span class="cx"> class SyncLevel (WebDAVTextElement):
</span><span class="lines">@@ -96,5 +106,29 @@
</span><span class="cx"> name = "sync-level"
</span><span class="cx">
</span><span class="cx">
</span><ins>+
+@registerElement
+@registerElementClass
+class Limit (WebDAVElement):
+ """
+ Synchronization limit in report.
+ """
+ name = "limit"
+
+ allowed_children = {
+ (dav_namespace, "nresults"): (1, 1), # When used in the REPORT this is required
+ }
+
+
+
+@registerElement
+@registerElementClass
+class NResults (WebDAVTextElement):
+ """
+ Synchronization numerical limit.
+ """
+ name = "nresults"
+
+
</ins><span class="cx"> # Extend MultiStatus, to add sync-token
</span><span class="cx"> MultiStatus.allowed_children[(dav_namespace, "sync-token")] = (0, 1)
</span></span></pre>
</div>
</div>
</body>
</html>